The first part of new l3 networking implement

1. What is the problem
In the current l3 networking model, the host route will be only valid
after DHCP lease time expired, and renewed.

2. What is the solution for the problem
To overcome the drawbacks, a new l3 networking model is introduced in
this spec(https://review.openstack.org/#/c/530904/).
This part of implement includes bottom external network creation after
segment-create and bottom external subnet creation after subnet-create
using segment.

3. What features need to be implemented to the Tricircle to
realize the solution
N/A
[Test record]http://note.youdao.com/noteshare?id=e2e5e4bf6e73db1bc55f47963a72b39d&sub=6158D53B085D4A7B8EB046F01FB62748

Change-Id: I7d968a402b23428b4d3a7061ec507ed30a6132ef
This commit is contained in:
lyman-xu 2018-05-17 19:00:57 +08:00
parent dca1fb1954
commit e57c5f497f
9 changed files with 747 additions and 32 deletions

View File

@ -228,3 +228,9 @@ CENTRAL = 'central-neutronclient'
LOCAL = 'local-neutronclient'
REQUEST_SOURCE_TYPE = set([CENTRAL, LOCAL])
# for new L3 network model using routed network
# prefix for the name of segment
SEGMENT_NAME_PATTERN = 'newL3-(.*?)-(.*)'
PREFIX_OF_SEGMENT_NAME = 'newL3-'
PREFIX_OF_SEGMENT_NAME_DIVISION = '-'

View File

@ -128,7 +128,13 @@ tricircle_opts = [
' to.')),
cfg.BoolOpt('enable_api_gateway',
default=True,
help=_('Whether the Nova API gateway is enabled'))
help=_('Whether the Nova API gateway is enabled')),
cfg.BoolOpt('enable_l3_route_network',
default=False,
help=_('Whether using the new L3 networking model. When it is'
'set to true, Tricircle will automatically create a'
'bottom external network if the name of segment'
'matches newL3-..'))
]
tricircle_opt_group = cfg.OptGroup('tricircle')
@ -267,24 +273,6 @@ class TricirclePlugin(db_base_plugin_v2.NeutronDbPluginV2,
if validators.is_attr_set(from_net.get(provider_attr)):
to_net[provider_attr] = from_net[provider_attr]
def _create_bottom_external_network(self, context, net, top_id):
t_ctx = t_context.get_context_from_neutron_context(context)
# use the first pod
az_name = net[az_def.AZ_HINTS][0]
pod = db_api.find_pod_by_az_or_region(t_ctx, az_name)
body = {
'network': {
'name': top_id,
'tenant_id': net['tenant_id'],
'admin_state_up': True,
external_net.EXTERNAL: True
}
}
self._fill_provider_info(net, body['network'])
self._prepare_bottom_element(
t_ctx, net['tenant_id'], pod, {'id': top_id},
t_constants.RT_NETWORK, body)
def _create_bottom_external_subnet(self, context, subnet, net, top_id):
t_ctx = t_context.get_context_from_neutron_context(context)
region_name = net[az_def.AZ_HINTS][0]
@ -303,7 +291,7 @@ class TricirclePlugin(db_base_plugin_v2.NeutronDbPluginV2,
for attr in attrs:
if validators.is_attr_set(subnet.get(attr)):
body['subnet'][attr] = subnet[attr]
self._prepare_bottom_element(
self.helper.prepare_bottom_element(
t_ctx, subnet['tenant_id'], pod, {'id': top_id},
t_constants.RT_SUBNET, body)
@ -340,7 +328,7 @@ class TricirclePlugin(db_base_plugin_v2.NeutronDbPluginV2,
if is_external:
self._fill_provider_info(res, net_data)
try:
self._create_bottom_external_network(
self.helper.prepare_bottom_external_network(
context, net_data, res['id'])
except q_cli_exceptions.Conflict as e:
pattern = re.compile('Physical network (.*) is in use')
@ -544,6 +532,7 @@ class TricirclePlugin(db_base_plugin_v2.NeutronDbPluginV2,
def create_subnet(self, context, subnet):
subnet_data = subnet['subnet']
network = self.get_network(context, subnet_data['network_id'])
is_external = network.get(external_net.EXTERNAL)
with context.session.begin(subtransactions=True):
res = super(TricirclePlugin, self).create_subnet(context, subnet)
@ -589,7 +578,8 @@ class TricirclePlugin(db_base_plugin_v2.NeutronDbPluginV2,
self.helper.get_real_shadow_resource_iterator(
t_ctx, t_constants.RT_SUBNET, subnet_id)):
region_name = pod['region_name']
self._get_client(region_name).delete_subnets(
b_client = self._get_client(region_name)
b_client.delete_subnets(
t_ctx, bottom_subnet_id)
interface_name = t_constants.interface_port_name % (
region_name, subnet_id)
@ -607,6 +597,7 @@ class TricirclePlugin(db_base_plugin_v2.NeutronDbPluginV2,
'value': pod['pod_id']}])
except Exception:
raise
dhcp_port_name = t_constants.dhcp_port_name % subnet_id
self._delete_pre_created_port(t_ctx, context, dhcp_port_name)
snat_port_name = t_constants.snat_port_name % subnet_id
@ -1546,11 +1537,6 @@ class TricirclePlugin(db_base_plugin_v2.NeutronDbPluginV2,
return self.helper.prepare_top_element(
t_ctx, q_ctx, project_id, pod, ele, _type, body)
def _prepare_bottom_element(self, t_ctx,
project_id, pod, ele, _type, body):
return self.helper.prepare_bottom_element(
t_ctx, project_id, pod, ele, _type, body)
def _get_bridge_subnet_pool_id(self, t_ctx, q_ctx, project_id, pod):
pool_name = t_constants.bridge_subnet_pool_name
pool_cidr = cfg.CONF.client.bridge_cidr
@ -1706,7 +1692,7 @@ class TricirclePlugin(db_base_plugin_v2.NeutronDbPluginV2,
body = {'router': {'name': t_constants.ns_router_name % router_id,
'distributed': False}}
_, b_router_id = self._prepare_bottom_element(
_, b_router_id = self.helper.prepare_bottom_element(
t_ctx, t_router['tenant_id'], pod, t_router, router_type, body)
# both router and external network in bottom pod are ready, attach

View File

@ -19,8 +19,11 @@ import re
import six
from six.moves import xrange
from neutron_lib.api.definitions import availability_zone as az_def
from neutron_lib.api.definitions import external_net
from neutron_lib.api.definitions import portbindings
from neutron_lib.api.definitions import provider_net
from neutron_lib.api import validators
from neutron_lib import constants
import neutronclient.common.exceptions as q_cli_exceptions
from oslo_serialization import jsonutils
@ -28,6 +31,7 @@ from oslo_serialization import jsonutils
from tricircle.common import client
import tricircle.common.constants as t_constants
import tricircle.common.context as t_context
import tricircle.common.exceptions as t_exceptions
import tricircle.common.lock_handle as t_lock
from tricircle.common import utils
import tricircle.db.api as db_api
@ -969,6 +973,154 @@ class NetworkHelper(object):
t_constants.RT_SD_PORT, create_body)
return sw_port_id
def prepare_bottom_router(self, n_context, net, b_router_name):
t_ctx = t_context.get_context_from_neutron_context(n_context)
# use the first pod
az_name = net[az_def.AZ_HINTS][0]
pod = db_api.find_pod_by_az_or_region(t_ctx, az_name)
body = {
'router': {
'name': b_router_name,
'tenant_id': net['tenant_id'],
'admin_state_up': True,
'distributed': False
}
}
return self.prepare_bottom_element(
t_ctx, net['tenant_id'], pod, {'id': b_router_name},
t_constants.RT_ROUTER, body)
def remove_bottom_router_by_name(self, n_context, region_name,
router_name):
t_ctx = t_context.get_context_from_neutron_context(n_context)
b_client = self._get_client(region_name)
bottom_router = b_client.list_routers(
t_ctx, [{'key': 'name',
'comparator': 'eq',
'value': router_name}])
if bottom_router:
b_client.delete_routers(t_ctx, bottom_router[0]['id'])
def _fill_provider_info(self, from_net, to_net):
provider_attrs = provider_net.ATTRIBUTES
for provider_attr in provider_attrs:
if validators.is_attr_set(from_net.get(provider_attr)):
to_net[provider_attr] = from_net[provider_attr]
if validators.is_attr_set(from_net.get(az_def.AZ_HINTS)):
to_net[az_def.AZ_HINTS] = from_net[az_def.AZ_HINTS]
def prepare_bottom_external_network(self, n_context, net, top_id):
t_ctx = t_context.get_context_from_neutron_context(n_context)
# use the first pod
az_name = net[az_def.AZ_HINTS][0]
pod = db_api.find_pod_by_az_or_region(t_ctx, az_name)
body = {
'network': {
'name': net['name'],
'tenant_id': net['tenant_id'],
'admin_state_up': True,
external_net.EXTERNAL: True,
}
}
self._fill_provider_info(net, body['network'])
return self.prepare_bottom_element(
t_ctx, net['tenant_id'], pod, {'id': top_id},
t_constants.RT_NETWORK, body)
def remove_bottom_external_network_by_name(
self, n_context, region_name, name):
t_ctx = t_context.get_context_from_neutron_context(n_context)
b_client = self._get_client(region_name)
b_net = b_client.list_networks(
t_ctx, [{'key': 'name',
'comparator': 'eq',
'value': name}])
if b_net:
b_client.delete_networks(t_ctx, b_net[0]['id'])
def prepare_bottom_external_subnet_by_bottom_name(
self, context, subnet, region_name, b_net_name, top_subnet_id):
t_ctx = t_context.get_context_from_neutron_context(context)
pod = db_api.get_pod_by_name(t_ctx, region_name)
b_client = self._get_client(region_name)
bottom_network = b_client.list_networks(
t_ctx, [{'key': 'name',
'comparator': 'eq',
'value': b_net_name}]
)
if not bottom_network:
raise t_exceptions.InvalidInput(
reason='bottom network not found for %(b_net_name)s'
% {'b_net_name': b_net_name})
body = {
'subnet': {
'name': top_subnet_id,
'network_id': bottom_network[0]['id'],
'tenant_id': subnet['tenant_id']
}
}
attrs = ('ip_version', 'cidr', 'gateway_ip', 'allocation_pools',
'enable_dhcp')
for attr in attrs:
if validators.is_attr_set(subnet.get(attr)):
body['subnet'][attr] = subnet[attr]
self.prepare_bottom_element(
t_ctx, subnet['tenant_id'], pod, {'id': top_subnet_id},
t_constants.RT_SUBNET, body)
def remove_bottom_external_subnet_by_name(
self, context, region_name, b_subnet_name):
t_ctx = t_context.get_context_from_neutron_context(context)
b_client = self._get_client(region_name)
bottom_subnet = b_client.list_subnets(
t_ctx, [{'key': 'name',
'comparator': 'eq',
'value': b_subnet_name}]
)
if bottom_subnet:
b_client.delete_subnets(t_ctx, bottom_subnet[0]['id'])
def prepare_bottom_router_gateway(
self, n_context, region_name, segment_name):
t_ctx = t_context.get_context_from_neutron_context(n_context)
pod = db_api.get_pod_by_name(t_ctx, region_name)
b_client = self._get_client(pod['region_name'])
# when using new l3 network model, a local router will
# be created automatically for an external net, and the
# router's name is same to the net's id
b_router = b_client.list_routers(
t_ctx, filters=[{'key': 'name', 'comparator': 'eq',
'value': segment_name}])
if not b_router:
raise t_exceptions.NotFound()
b_nets = b_client.list_networks(
t_ctx, filters=[{'key': 'name',
'comparator': 'eq',
'value': segment_name}]
)
if not b_nets:
raise t_exceptions.NotFound()
b_info = {'network_id': b_nets[0]['id']}
return b_client.action_routers(
t_ctx, 'add_gateway', b_router[0]['id'], b_info)
def remove_bottom_router_gateway(
self, n_context, region_name, b_net_name):
t_ctx = t_context.get_context_from_neutron_context(n_context)
pod = db_api.get_pod_by_name(t_ctx, region_name)
b_client = self._get_client(pod['region_name'])
# when using new l3 network model, a local router will
# be created automatically for an external net, and the
# router's name is same to the net's id
b_router = b_client.list_routers(
t_ctx, filters=[{'key': 'name', 'comparator': 'eq',
'value': b_net_name}])
if not b_router:
raise t_exceptions.NotFound()
return b_client.action_routers(
t_ctx, 'remove_gateway', b_router[0]['id'], b_router[0]['id'])
@staticmethod
def get_real_shadow_resource_iterator(t_ctx, res_type, res_id):
shadow_res_type = None

View File

@ -23,9 +23,12 @@ from neutron_lib.api.definitions import portbindings
from neutron_lib.api.definitions import provider_net
from neutron_lib.api import extensions
from neutron_lib.api import validators
from neutron_lib.callbacks import events
import neutron_lib.constants as q_constants
import neutron_lib.exceptions as q_exceptions
from neutron_lib.plugins import directory
from neutron_lib.plugins.ml2 import api
from neutron_lib.utils import net
from neutron_lib.utils import runtime
import neutronclient.client as neutronclient
@ -892,6 +895,8 @@ class TricirclePlugin(plugin.Ml2Plugin):
def _handle_security_group(self, t_ctx, q_ctx, port):
if 'security_groups' not in port:
return
if port.get('device_owner') and net.is_port_trusted(port):
return
if not port['security_groups']:
raw_client = self.neutron_handle._get_client(t_ctx)
params = {'name': 'default'}
@ -955,3 +960,23 @@ class TricirclePlugin(plugin.Ml2Plugin):
b_sgs.append(self.core_plugin.get_security_group(
context, b_sg['id'], fields))
return b_sgs
def _handle_segment_change(self, rtype, event, trigger, context, segment):
network_id = segment.get('network_id')
if event == events.PRECOMMIT_CREATE:
updated_segment = self.type_manager.reserve_network_segment(
context, segment)
# The segmentation id might be from ML2 type driver, update it
# in the original segment.
segment[api.SEGMENTATION_ID] = updated_segment[api.SEGMENTATION_ID]
elif event == events.PRECOMMIT_DELETE:
self.type_manager.release_network_segment(context, segment)
# change in segments could affect resulting network mtu, so let's
# recalculate it
network_db = self._get_network(context, network_id)
network_db.mtu = self._get_network_mtu(
network_db, validate=(event != events.PRECOMMIT_DELETE))
network_db.save(session=context.session)

View File

@ -0,0 +1,144 @@
# Copyright 2018 Huazhong University of Science and Technology.
# 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
import re
from neutron.services.segments.plugin import Plugin
from neutron_lib.api.definitions import availability_zone as az_def
from neutron_lib.api.definitions import provider_net
from neutron_lib.exceptions import availability_zone as az_exc
import tricircle.common.client as t_client
from tricircle.common import constants
import tricircle.common.context as t_context
from tricircle.common import xrpcapi
from tricircle.db import core
from tricircle.db import models
from tricircle.network.central_plugin import TricirclePlugin
from tricircle.network import helper
LOG = log.getLogger(__name__)
class TricircleSegmentPlugin(Plugin):
def __init__(self):
super(TricircleSegmentPlugin, self).__init__()
self.xjob_handler = xrpcapi.XJobAPI()
self.clients = {}
self.central_plugin = TricirclePlugin()
self.helper = helper.NetworkHelper(self)
def _get_client(self, region_name):
if region_name not in self.clients:
self.clients[region_name] = t_client.Client(region_name)
return self.clients[region_name]
def get_segment(self, context, sgmt_id, fields=None, tenant_id=None):
return super(TricircleSegmentPlugin, self).get_segment(
context, sgmt_id)
def get_segments(self, context, filters=None, fields=None,
sorts=None, limit=None, marker=None,
page_reverse=False):
return super(TricircleSegmentPlugin, self).get_segments(
context, filters, fields, sorts, limit, marker, page_reverse)
@staticmethod
def _validate_availability_zones(context, az_list):
if not az_list:
return
t_ctx = t_context.get_context_from_neutron_context(context)
with context.session.begin(subtransactions=True):
pods = core.query_resource(t_ctx, models.Pod, [], [])
az_set = set(az_list)
known_az_set = set([pod['az_name'] for pod in pods])
known_az_set = known_az_set | set(
[pod['region_name'] for pod in pods])
diff = az_set - known_az_set
if diff:
raise az_exc.AvailabilityZoneNotFound(
availability_zone=diff.pop())
def create_segment(self, context, segment):
"""Create a segment."""
segment_data = segment['segment']
segment_name = segment_data.get('name')
# if configed enable_l3_route_network,
# will create real external network for each segment
if cfg.CONF.tricircle.enable_l3_route_network:
match_obj = re.match(constants.SEGMENT_NAME_PATTERN,
segment_name)
if match_obj:
match_list = match_obj.groups()
region_name = match_list[0]
self._validate_availability_zones(context,
[region_name])
# create segment for maintaining the relationship
# between routed net and real external net
segment_db = super(TricircleSegmentPlugin, self).\
create_segment(context, segment)
# prepare real external network in central and bottom
net_data = {
'tenant_id': segment_data.get('tenant_id'),
'name': segment_name,
'shared': False,
'admin_state_up': True,
az_def.AZ_HINTS: [region_name],
provider_net.PHYSICAL_NETWORK:
segment_data.get('physical_network'),
provider_net.NETWORK_TYPE:
segment_data.get('network_type'),
'router:external': True
}
self.central_plugin.create_network(
context, {'network': net_data})
return segment_db
else:
return super(TricircleSegmentPlugin, self).create_segment(
context, segment)
else:
return super(TricircleSegmentPlugin, self).create_segment(
context, segment)
def delete_segment(self, context, uuid, for_net_delete=False):
segment_dict = self.get_segment(context, uuid)
segment_name = segment_dict['name']
# if enable l3 routed network and segment name starts
# with 'newl3-' need to delete bottom router
# and bottom external network
if cfg.CONF.tricircle.enable_l3_route_network and \
segment_name and \
segment_name.startswith(constants.PREFIX_OF_SEGMENT_NAME):
# delete real external network
net_filter = {'name': [segment_name]}
nets = self.central_plugin.get_networks(context, net_filter)
if len(nets):
self.central_plugin.delete_network(context, nets[0]['id'])
return super(TricircleSegmentPlugin, self).delete_segment(
context, uuid)
else:
return super(TricircleSegmentPlugin, self).delete_segment(
context, uuid)

View File

@ -367,6 +367,13 @@ class FakeClient(test_utils.FakeClient):
if index != -1:
del TOP_IPALLOCATIONS[index]
def dhcp_allocate_ip(self, subnets):
fixed_ips = []
for subnet in subnets:
fixed_ips.append({'subnet_id': subnet['id'],
'ip_address': '10.0.0.1'})
return fixed_ips
def add_gateway_routers(self, ctx, *args, **kwargs):
router_id, body = args
try:
@ -376,6 +383,14 @@ class FakeClient(test_utils.FakeClient):
ctx, [{'key': 'name', 'comparator': 'eq', 'value': t_name}])
b_id = t_ports[0]['id'] if t_ports else uuidutils.generate_uuid()
host_id = 'host1' if self.region_name == 'pod_1' else 'host_2'
if not body.get('external_fixed_ips'):
net_id = body['network_id']
subnets = self.list_subnets(ctx,
[{'key': 'network_id',
'comparator': 'eq',
'value': net_id}])
body['external_fixed_ips'] = self.dhcp_allocate_ip(subnets)
self.create_ports(ctx, {'port': {
'admin_state_up': True,
'id': b_id,
@ -427,6 +442,9 @@ class FakeClient(test_utils.FakeClient):
router = self.get_resource(constants.RT_ROUTER, ctx, router_id)
return _fill_external_gateway_info(router)
def list_routers(self, ctx, filters=None):
return self.list_resources('router', ctx, filters)
def delete_routers(self, ctx, router_id):
self.delete_resources('router', ctx, router_id)
@ -985,6 +1003,7 @@ class PluginTest(unittest.TestCase,
xmanager.IN_TEST = True
phynet = 'bridge'
phynet2 = 'bridge2'
vlan_min, vlan_max = 2000, 2001
vxlan_min, vxlan_max = 20001, 20002
cfg.CONF.set_override('type_drivers', ['local', 'vlan'],
@ -992,7 +1011,8 @@ class PluginTest(unittest.TestCase,
cfg.CONF.set_override('tenant_network_types', ['local', 'vlan'],
group='tricircle')
cfg.CONF.set_override('network_vlan_ranges',
['%s:%d:%d' % (phynet, vlan_min, vlan_max)],
['%s:%d:%d' % (phynet, vlan_min, vlan_max),
'%s:%d:%d' % (phynet2, vlan_min, vlan_max)],
group='tricircle')
cfg.CONF.set_override('bridge_network_type', 'vlan',
group='tricircle')
@ -2959,8 +2979,8 @@ class PluginTest(unittest.TestCase,
'external_fixed_ips': [{'subnet_id': b_subnet_id,
'ip_address': '100.64.0.5'}]}
mock_action.assert_called_once_with(t_ctx, 'add_gateway',
b_router_id, body)
mock_action.assert_called_with(t_ctx, 'add_gateway',
b_router_id, body)
self.assertFalse(mock_get_bridge_network.called)
@patch.object(directory, 'get_plugin', new=fake_get_plugin)

View File

@ -260,6 +260,7 @@ class FakePlugin(plugin.TricirclePlugin):
self.neutron_handle = FakeNeutronHandle()
self.on_trunk_create = {}
self.on_subnet_delete = {}
self.type_manager = test_utils.FakeTypeManager()
class PluginTest(unittest.TestCase):
@ -267,6 +268,11 @@ class PluginTest(unittest.TestCase):
self.tenant_id = uuidutils.generate_uuid()
self.plugin = FakePlugin()
self.context = FakeContext()
phynet2 = 'provider2'
vlan_min, vlan_max = 2000, 3000
cfg.CONF.set_override('network_vlan_ranges',
['%s:%d:%d' % (phynet2, vlan_min, vlan_max)],
group='tricircle')
def _prepare_resource(self, az_hints=None, enable_dhcp=True):
network_id = uuidutils.generate_uuid()

View File

@ -0,0 +1,375 @@
# Copyright 2018 Huazhong University of Science and Technology.
# 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 mock import patch
import unittest
from neutron_lib.api.definitions import provider_net
from neutron_lib.plugins import constants as plugin_constants
from neutron_lib.plugins import directory
import neutron.conf.common as q_config
from neutron.extensions import segment as extension
from neutron.plugins.ml2 import managers as n_managers
from neutron.services.segments import exceptions as sg_excp
from oslo_config import cfg
from oslo_serialization import jsonutils
from oslo_utils import uuidutils
from tricircle.common import constants as t_constant
from tricircle.common import context
import tricircle.db.api as db_api
from tricircle.db import core
from tricircle.db import models
import tricircle.network.central_plugin as plugin
from tricircle.network import helper
from tricircle.network.segment_plugin import TricircleSegmentPlugin
from tricircle.tests.unit.network.test_central_plugin \
import FakeClient as CentralFakeClient
from tricircle.tests.unit.network.test_central_plugin \
import FakePlugin as CentralFakePlugin
import tricircle.tests.unit.utils as test_utils
_resource_store = test_utils.get_resource_store()
TOP_NETS = _resource_store.TOP_NETWORKS
TOP_SUBNETS = _resource_store.TOP_SUBNETS
TOP_PORTS = _resource_store.TOP_PORTS
TOP_ROUTERS = _resource_store.TOP_ROUTERS
TOP_SEGMENTS = _resource_store.TOP_NETWORKSEGMENTS
BOTTOM1_NETS = _resource_store.BOTTOM1_NETWORKS
BOTTOM1_SUBNETS = _resource_store.BOTTOM1_SUBNETS
BOTTOM1_PORTS = _resource_store.BOTTOM1_PORTS
TEST_TENANT_ID = test_utils.TEST_TENANT_ID
FakeNeutronContext = test_utils.FakeNeutronContext
TEST_TENANT_ID = test_utils.TEST_TENANT_ID
class FakeClient(CentralFakeClient):
def __init__(self, region_name=None):
super(FakeClient, self).__init__(region_name)
def delete_segments(self, ctx, segment_id):
self.delete_resources('segment', ctx, segment_id)
class FakeExtensionManager(n_managers.ExtensionManager):
def __init__(self):
super(FakeExtensionManager, self).__init__()
class FakeHelper(helper.NetworkHelper):
def _get_client(self, region_name=None):
return FakeClient(region_name)
class FakeTrunkPlugin(object):
def get_trunk_subports(self, context, filters):
return None
class FakePlugin(TricircleSegmentPlugin):
def start_rpc_state_reports_listener(self):
pass
def __init__(self):
self.type_manager = test_utils.FakeTypeManager()
self.extension_manager = FakeExtensionManager()
self.extension_manager.initialize()
self.helper = FakeHelper(self)
self.central_plugin = CentralFakePlugin()
def _get_client(self, region_name):
return FakeClient(region_name)
@staticmethod
def get_network_availability_zones(network):
zones = network.get('availability_zone_hints') \
if network.get('availability_zone_hints') else []
return list(zones)
def _make_network_dict(self, network, fields=None,
process_extensions=True, context=None):
network = _transform_az(network)
if 'project_id' in network:
network['tenant_id'] = network['project_id']
return network
def fake_get_plugin(alias=plugin_constants.CORE):
return CentralFakePlugin()
def fake_get_client(region_name):
return FakeClient(region_name)
def fake_get_context_from_neutron_context(q_context):
return context.get_db_context()
def _transform_az(network):
az_hints_key = 'availability_zone_hints'
if az_hints_key in network:
ret = test_utils.DotDict(network)
az_str = network[az_hints_key]
ret[az_hints_key] = jsonutils.loads(az_str) if az_str else []
return ret
return network
def fake_delete_network(self, context, network_id):
fake_client = FakeClient()
fake_client.delete_networks(context, network_id)
class PluginTest(unittest.TestCase):
def setUp(self):
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)
cfg.CONF.set_override('enable_l3_route_network', True,
group='tricircle')
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()
phynet = 'bridge'
phynet2 = 'bridge2'
vlan_min, vlan_max = 2000, 3000
cfg.CONF.set_override('type_drivers', ['local', 'vlan'],
group='tricircle')
cfg.CONF.set_override('tenant_network_types', ['local', 'vlan'],
group='tricircle')
cfg.CONF.set_override('network_vlan_ranges',
['%s:%d:%d' % (phynet, vlan_min, vlan_max),
'%s:%d:%d' % (phynet2, vlan_min, vlan_max)],
group='tricircle')
cfg.CONF.set_override('bridge_network_type', 'vlan',
group='tricircle')
def fake_get_plugin(alias=plugin_constants.CORE):
if alias == 'trunk':
return FakeTrunkPlugin()
return CentralFakePlugin()
from neutron_lib.plugins import directory
directory.get_plugin = fake_get_plugin
global segments_plugin
segments_plugin = FakePlugin()
def _basic_pod_route_setup(self):
pod1 = {'pod_id': 'pod_id_1',
'region_name': 'pod_1',
'az_name': 'az_name_1'}
pod2 = {'pod_id': 'pod_id_2',
'region_name': 'pod_2',
'az_name': 'az_name_2'}
pod3 = {'pod_id': 'pod_id_0',
'region_name': 'top_pod',
'az_name': ''}
for pod in (pod1, pod2, pod3):
db_api.create_pod(self.context, pod)
route1 = {
'top_id': 'top_id_1',
'pod_id': 'pod_id_1',
'bottom_id': 'bottom_id_1',
'resource_type': 'port'}
route2 = {
'top_id': 'top_id_2',
'pod_id': 'pod_id_2',
'bottom_id': 'bottom_id_2',
'resource_type': 'port'}
with self.context.session.begin():
core.create_resource(self.context, models.ResourceRouting, route1)
core.create_resource(self.context, models.ResourceRouting, route2)
@patch.object(directory, 'get_plugin', new=fake_get_plugin)
@patch.object(context, 'get_context_from_neutron_context')
@patch.object(TricircleSegmentPlugin, '_get_client',
new=fake_get_client)
@patch.object(plugin.TricirclePlugin, '_get_client',
new=fake_get_client)
def test_create_segment(self, mock_context):
self._basic_pod_route_setup()
fake_plugin = FakePlugin()
neutron_context = FakeNeutronContext()
tricircle_context = context.get_db_context()
mock_context.return_value = tricircle_context
# create a routed network
top_net_id = uuidutils.generate_uuid()
network = {'network': {
'id': top_net_id, 'name': 'multisegment1',
'tenant_id': TEST_TENANT_ID,
'admin_state_up': True, 'shared': False,
'availability_zone_hints': [],
provider_net.PHYSICAL_NETWORK: 'bridge',
provider_net.NETWORK_TYPE: 'vlan',
provider_net.SEGMENTATION_ID: '2016'}}
fake_plugin.central_plugin.create_network(neutron_context, network)
net_filter = {'name': ['multisegment1']}
top_net = fake_plugin.central_plugin.get_networks(
neutron_context, net_filter)
self.assertEqual(top_net[0]['id'], top_net_id)
res = fake_plugin.get_segments(neutron_context)
self.assertEqual(len(res), 1)
# success
# segment's name matches 'newl3-regionname-detailname'
segment2_id = uuidutils.generate_uuid()
segment2_name = t_constant.PREFIX_OF_SEGMENT_NAME + 'pod_1' \
+ t_constant.PREFIX_OF_SEGMENT_NAME_DIVISION \
+ 'segment2'
segment2 = {'segment': {
'id': segment2_id,
'name': segment2_name,
'network_id': top_net_id,
extension.PHYSICAL_NETWORK: 'bridge2',
extension.NETWORK_TYPE: 'flat',
extension.SEGMENTATION_ID: '2016',
'tenant_id': TEST_TENANT_ID,
'description': None
}}
fake_plugin.create_segment(neutron_context, segment2)
res = fake_plugin.get_segment(neutron_context, segment2_id)
self.assertEqual(res['name'], segment2_name)
net_filter = {'name': [segment2_name]}
b_net = fake_plugin.central_plugin.get_networks(
neutron_context, net_filter)
self.assertEqual(b_net[0]['name'], segment2_name)
# create segments normally
# segment's name doesn't match 'newl3-regionname-detailname'
segment3_id = uuidutils.generate_uuid()
segment3_name = 'test-segment3'
segment3 = {'segment': {
'id': segment3_id,
'name': segment3_name,
'network_id': top_net_id,
extension.PHYSICAL_NETWORK: 'bridge2',
extension.NETWORK_TYPE: 'flat',
extension.SEGMENTATION_ID: '2016',
'tenant_id': TEST_TENANT_ID,
'description': None
}}
fake_plugin.create_segment(neutron_context, segment3)
res = fake_plugin.get_segment(neutron_context, segment3_id)
self.assertEqual(res['name'], segment3_name)
net_filter = {'name': [segment3_name]}
b_net = fake_plugin.central_plugin.get_networks(
neutron_context, net_filter)
self.assertFalse(b_net)
@patch.object(directory, 'get_plugin', new=fake_get_plugin)
@patch.object(context, 'get_context_from_neutron_context')
@patch.object(TricircleSegmentPlugin, '_get_client',
new=fake_get_client)
@patch.object(plugin.TricirclePlugin, '_get_client',
new=fake_get_client)
@patch.object(plugin.TricirclePlugin, 'delete_network',
new=fake_delete_network)
def test_delete_segment(self, mock_context):
self._basic_pod_route_setup()
fake_plugin = FakePlugin()
neutron_context = FakeNeutronContext()
tricircle_context = context.get_db_context()
mock_context.return_value = tricircle_context
# create a routed network
top_net_id = uuidutils.generate_uuid()
network = {'network': {
'id': top_net_id, 'name': 'multisegment1',
'tenant_id': TEST_TENANT_ID,
'admin_state_up': True, 'shared': False,
'availability_zone_hints': [],
provider_net.PHYSICAL_NETWORK: 'bridge',
provider_net.NETWORK_TYPE: 'vlan',
provider_net.SEGMENTATION_ID: '2016'}}
fake_plugin.central_plugin.create_network(neutron_context, network)
# create a normal segment
segment2_id = uuidutils.generate_uuid()
segment2_name = 'test-segment3'
segment2 = {'segment': {
'id': segment2_id,
'name': segment2_name,
'network_id': top_net_id,
extension.PHYSICAL_NETWORK: 'bridge2',
extension.NETWORK_TYPE: 'flat',
extension.SEGMENTATION_ID: '2016',
'tenant_id': TEST_TENANT_ID,
'description': None
}}
fake_plugin.create_segment(neutron_context, segment2)
# create a segment
# with it's name matches 'newl3-regionname-detailname'
segment3_id = uuidutils.generate_uuid()
segment3_name = t_constant.PREFIX_OF_SEGMENT_NAME + 'pod_1'\
+ t_constant.PREFIX_OF_SEGMENT_NAME_DIVISION + 'segment2'
segment3 = {'segment': {
'id': segment3_id,
'name': segment3_name,
'network_id': top_net_id,
extension.PHYSICAL_NETWORK: 'bridge2',
extension.NETWORK_TYPE: 'flat',
extension.SEGMENTATION_ID: '2016',
'tenant_id': TEST_TENANT_ID,
'description': None
}}
fake_plugin.create_segment(neutron_context, segment3)
res = fake_plugin.get_segment(neutron_context, segment2_id)
self.assertEqual(res['name'], segment2_name)
res = fake_plugin.get_segment(neutron_context, segment3_id)
self.assertEqual(res['name'], segment3_name)
net_filter = {'name': [segment2_name]}
b_net = fake_plugin.central_plugin.get_networks(
neutron_context, net_filter)
self.assertFalse(b_net)
net_filter = {'name': [segment3_name]}
b_net = fake_plugin.central_plugin.get_networks(
neutron_context, net_filter)
self.assertEqual(b_net[0]['name'], segment3_name)
# delete a segment
# it's name matches 'newl3-regionname-detailname'
fake_plugin.delete_segment(neutron_context, segment3_id)
self.assertRaises(sg_excp.SegmentNotFound,
fake_plugin.get_segment,
neutron_context, segment3_id)
net_filter = {'name': [segment3_name]}
b_net = fake_plugin.central_plugin.get_networks(
neutron_context, net_filter)
self.assertFalse(b_net)
# delete a normal segment
fake_plugin.delete_segment(neutron_context, segment2_id)
self.assertRaises(sg_excp.SegmentNotFound,
fake_plugin.get_segment,
neutron_context, segment2_id)
def tearDown(self):
core.ModelBase.metadata.drop_all(core.get_engine())
test_utils.get_resource_store().clean()
cfg.CONF.unregister_opts(q_config.core_opts)
cfg.CONF.unregister_opts(plugin.tricircle_opts)

View File

@ -65,7 +65,8 @@ class ResourceStore(object):
('sfc_chain_classifier_associations', None),
('qos_policies', constants.RT_QOS),
('qos_bandwidth_limit_rules',
'qos_bandwidth_limit_rules')]
'qos_bandwidth_limit_rules'),
('segments', None)]
def __init__(self):
self.store_list = []