Multiple pods share the same physical network name

1. What is the problem?
Multiple pods may be configured with physical networks that have
the same name, so theoretically external networks in different
pods can have the same physical network name. But currently we
can not create such external networks, because central Neutron
will save the used physical network in the database, when creating
the second external networks, FlatNetworkInUse exception will be
raised.

2. What is the solution to the problem?
Catch FlatNetworkInUse exception and leave the validation of
physical network to local Neutron.

3. What features need to be implemented to the Tricircle
to realize the solution?
Now users can create flat external networks with the same physical
network name in different pods.

Change-Id: Icf7877bc6cef8757b82552f9c3871336442a07a6
This commit is contained in:
zhiyuan_cai 2017-04-20 11:49:28 +08:00
parent d281fdd1b6
commit e85d91362b
3 changed files with 71 additions and 7 deletions

View File

@ -15,6 +15,7 @@
import collections
import copy
import re
import six
from oslo_config import cfg
@ -26,6 +27,7 @@ from neutron.api.v2 import attributes
from neutron.callbacks import events
from neutron.callbacks import registry
from neutron.callbacks import resources
import neutron.common.exceptions as ml2_exceptions
from neutron.db import api as q_db_api
from neutron.db.availability_zone import router as router_az
from neutron.db import common_db_mixin
@ -314,8 +316,17 @@ class TricirclePlugin(db_base_plugin_v2.NeutronDbPluginV2,
# rollback
if is_external:
self._fill_provider_info(res, net_data)
self._create_bottom_external_network(
context, net_data, res['id'])
try:
self._create_bottom_external_network(
context, net_data, res['id'])
except q_cli_exceptions.Conflict as e:
pattern = re.compile('Physical network (.*) is in use')
match = pattern.search(e.message)
if not match:
raise
else:
raise ml2_exceptions.FlatNetworkInUse(
physical_network=match.groups()[0])
return res
def delete_network(self, context, network_id):

View File

@ -16,6 +16,7 @@
from oslo_config import cfg
from oslo_log import log
from neutron.common import exceptions
from neutron.plugins.ml2 import driver_api
from neutron.plugins.ml2.drivers import type_flat
@ -36,8 +37,15 @@ class FlatTypeDriver(type_flat.FlatTypeDriver):
LOG.info("FlatTypeDriver initialization complete")
def reserve_provider_segment(self, context, segment):
res = super(FlatTypeDriver,
self).reserve_provider_segment(context, segment)
try:
res = super(FlatTypeDriver,
self).reserve_provider_segment(context, segment)
except exceptions.FlatNetworkInUse:
# to support multiple regions sharing the same physical network
# for external network, we ignore this exception and let local
# Neutron judge whether the physical network is valid
res = segment
res[driver_api.MTU] = None
res[driver_api.NETWORK_TYPE] = self.get_type()
return res

View File

@ -28,13 +28,13 @@ from sqlalchemy.sql import elements
from sqlalchemy.sql import selectable
from neutron_lib.api.definitions import portbindings
from neutron_lib.api.definitions import provider_net
import neutron_lib.constants as q_constants
import neutron_lib.context as q_context
import neutron_lib.exceptions as q_lib_exc
from neutron_lib.plugins import directory
import neutron.conf.common as q_config
from neutron.db import _utils
from neutron.db import db_base_plugin_common
from neutron.db import db_base_plugin_v2
@ -42,6 +42,7 @@ from neutron.db import ipam_pluggable_backend
from neutron.db import l3_db
from neutron.db import models_v2
from neutron.db import rbac_db_models as rbac_db
import neutron.objects.exceptions as q_obj_exceptions
from neutron.extensions import availability_zone as az_ext
@ -66,6 +67,7 @@ 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.drivers import type_flat
from tricircle.network.drivers import type_local
from tricircle.network.drivers import type_vlan
from tricircle.network.drivers import type_vxlan
@ -84,6 +86,7 @@ TOP_SUBNETPOOLPREFIXES = []
TOP_IPALLOCATIONS = []
TOP_VLANALLOCATIONS = []
TOP_VXLANALLOCATIONS = []
TOP_FLATALLOCATIONS = []
TOP_SEGMENTS = []
TOP_EXTNETS = []
TOP_FLOATINGIPS = []
@ -106,8 +109,8 @@ BOTTOM2_SGS = []
BOTTOM2_FIPS = []
RES_LIST = [TOP_NETS, TOP_SUBNETS, TOP_PORTS, TOP_ROUTERS, TOP_ROUTERPORT,
TOP_SUBNETPOOLS, TOP_SUBNETPOOLPREFIXES, TOP_IPALLOCATIONS,
TOP_VLANALLOCATIONS, TOP_VXLANALLOCATIONS, TOP_SEGMENTS,
TOP_EXTNETS, TOP_FLOATINGIPS, TOP_SGS, TOP_SG_RULES,
TOP_VLANALLOCATIONS, TOP_VXLANALLOCATIONS, TOP_FLOATINGIPS,
TOP_SEGMENTS, TOP_EXTNETS, TOP_FLOATINGIPS, TOP_SGS, TOP_SG_RULES,
TOP_NETWORK_RBAC, TOP_SUBNETROUTES, TOP_DNSNAMESERVERS,
BOTTOM1_NETS, BOTTOM1_SUBNETS, BOTTOM1_PORTS, BOTTOM1_ROUTERS,
BOTTOM1_SGS, BOTTOM1_FIPS,
@ -123,6 +126,7 @@ RES_MAP = {'networks': TOP_NETS,
'subnetpoolprefixes': TOP_SUBNETPOOLPREFIXES,
'ml2_vlan_allocations': TOP_VLANALLOCATIONS,
'ml2_vxlan_allocations': TOP_VXLANALLOCATIONS,
'ml2_flat_allocations': TOP_FLATALLOCATIONS,
'networksegments': TOP_SEGMENTS,
'externalnetworks': TOP_EXTNETS,
'floatingips': TOP_FLOATINGIPS,
@ -996,6 +1000,13 @@ class FakeSession(object):
subnet['dns_nameservers'].append(dnsnameservers)
break
if model_obj.__tablename__ == 'ml2_flat_allocations':
for alloc in TOP_FLATALLOCATIONS:
if alloc['physical_network'] == model_dict['physical_network']:
raise q_obj_exceptions.NeutronDbObjectDuplicateEntry(
model_obj.__class__,
DotDict({'columns': '', 'value': ''}))
self._extend_standard_attr(model_dict)
RES_MAP[model_obj.__tablename__].append(model_dict)
@ -1114,6 +1125,8 @@ class FakeTypeManager(managers.TricircleTypeManager):
self.drivers[constants.NT_VLAN] = FakeExtension(vlan_driver)
vxlan_driver = type_vxlan.VxLANTypeDriver()
self.drivers[constants.NT_VxLAN] = FakeExtension(vxlan_driver)
local_driver = type_flat.FlatTypeDriver()
self.drivers[constants.NT_FLAT] = FakeExtension(local_driver)
def extend_network_dict_provider(self, cxt, net):
target_net = None
@ -2899,6 +2912,38 @@ class PluginTest(unittest.TestCase,
t_ctx, top_net['id'], constants.RT_NETWORK)
self.assertEqual(mappings[0][1], bottom_net['id'])
@patch.object(directory, 'get_plugin', new=fake_get_plugin)
@patch.object(context, 'get_context_from_neutron_context')
def test_create_flat_external_network(self, mock_context):
self._basic_pod_route_setup()
fake_plugin = FakePlugin()
q_ctx = FakeNeutronContext()
t_ctx = context.get_db_context()
mock_context.return_value = t_ctx
body = {
'network': {
'name': 'ext-net1',
'admin_state_up': True,
'shared': False,
'tenant_id': TEST_TENANT_ID,
'router:external': True,
'availability_zone_hints': ['pod_1'],
provider_net.PHYSICAL_NETWORK: 'extern',
provider_net.NETWORK_TYPE: 'flat'
}
}
fake_plugin.create_network(q_ctx, body)
body['network']['name'] = ['ext-net2']
body['network']['availability_zone_hints'] = ['pod_2']
fake_plugin.create_network(q_ctx, body)
# we have ignore the FlatNetworkInUse exception, so only one allocation
# record is created, and both pods have one external network
self.assertEqual(1, len(TOP_FLATALLOCATIONS))
self.assertEqual(1, len(BOTTOM1_NETS))
self.assertEqual(1, len(BOTTOM2_NETS))
def _prepare_external_net_router_test(self, q_ctx, fake_plugin,
router_az_hints=None):