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:
parent
d281fdd1b6
commit
e85d91362b
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
||||
|
|
Loading…
Reference in New Issue