Enable router az and simplify net topology
1. What is the problem 1) Tricircle does not enable router's az 2) Tricircle's network topology is too complex for local router(router reside only in one region) 2. What is the solution to the problem 1) Enable router's az 2) Remove ns-router and bridge network when used local router 3. What the features need to be implemented to the Tricircle 1)Enable router's az 2)Attach external network to local router directly, no additional intermediate router is needed. Implements: blueprint enable-router-az-simplify-net-top Change-Id: I410a81c9d0e56db8163e611211b8dbd4c5772767
This commit is contained in:
parent
9e60abfe8f
commit
da443110e9
@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Router
|
||||||
|
|
||||||
|
* Support availability zone for router
|
||||||
|
* Local router, which will reside only inside one region, can be
|
||||||
|
attached with external network directly, no additional intermediate
|
||||||
|
router is needed.
|
@ -19,10 +19,13 @@ Tricircle base exception handling.
|
|||||||
|
|
||||||
import six
|
import six
|
||||||
|
|
||||||
|
from neutron_lib import exceptions
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
|
||||||
from tricircle.common.i18n import _
|
from tricircle.common.i18n import _
|
||||||
from tricircle.common.i18n import _LE
|
from tricircle.common.i18n import _LE
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@ -233,3 +236,12 @@ class RoutingBindFail(TricircleException):
|
|||||||
|
|
||||||
def __init__(self, _type):
|
def __init__(self, _type):
|
||||||
super(RoutingBindFail, self).__init__(_type=_type)
|
super(RoutingBindFail, self).__init__(_type=_type)
|
||||||
|
|
||||||
|
|
||||||
|
class RouterNetworkLocationMismatch(exceptions.InvalidInput):
|
||||||
|
message = _("router located in %(router_az_hint)s, but network located "
|
||||||
|
"in %(net_az_hints)s, location mismatch.")
|
||||||
|
|
||||||
|
def __init__(self, router_az_hints, net_az_hints):
|
||||||
|
super(RouterNetworkLocationMismatch, self).__init__(
|
||||||
|
router_az_hint=router_az_hints, net_az_hints=net_az_hints)
|
||||||
|
@ -23,6 +23,10 @@ import oslo_log.helpers as log_helpers
|
|||||||
from oslo_log import log
|
from oslo_log import log
|
||||||
|
|
||||||
from neutron.api.v2 import attributes
|
from neutron.api.v2 import attributes
|
||||||
|
from neutron.callbacks import events
|
||||||
|
from neutron.callbacks import registry
|
||||||
|
from neutron.callbacks import resources
|
||||||
|
from neutron.db.availability_zone import router as router_az
|
||||||
from neutron.db import common_db_mixin
|
from neutron.db import common_db_mixin
|
||||||
from neutron.db import db_base_plugin_v2
|
from neutron.db import db_base_plugin_v2
|
||||||
from neutron.db import external_net_db
|
from neutron.db import external_net_db
|
||||||
@ -30,7 +34,6 @@ from neutron.db import extradhcpopt_db
|
|||||||
# NOTE(zhiyuan) though not used, this import cannot be removed because Router
|
# NOTE(zhiyuan) though not used, this import cannot be removed because Router
|
||||||
# relies on one table defined in l3_agentschedulers_db
|
# relies on one table defined in l3_agentschedulers_db
|
||||||
from neutron.db import l3_agentschedulers_db # noqa
|
from neutron.db import l3_agentschedulers_db # noqa
|
||||||
from neutron.db import l3_attrs_db
|
|
||||||
from neutron.db import l3_db
|
from neutron.db import l3_db
|
||||||
from neutron.db import l3_dvr_db
|
from neutron.db import l3_dvr_db
|
||||||
# import l3_hamode_db to load l3_ha option
|
# import l3_hamode_db to load l3_ha option
|
||||||
@ -112,7 +115,7 @@ class TricirclePlugin(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
portbindings_db.PortBindingMixin,
|
portbindings_db.PortBindingMixin,
|
||||||
extradhcpopt_db.ExtraDhcpOptMixin,
|
extradhcpopt_db.ExtraDhcpOptMixin,
|
||||||
l3_db.L3_NAT_dbonly_mixin,
|
l3_db.L3_NAT_dbonly_mixin,
|
||||||
l3_attrs_db.ExtraAttributesMixin):
|
router_az.RouterAvailabilityZoneMixin):
|
||||||
|
|
||||||
__native_bulk_support = True
|
__native_bulk_support = True
|
||||||
__native_pagination_support = True
|
__native_pagination_support = True
|
||||||
@ -131,7 +134,14 @@ class TricirclePlugin(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
"provider",
|
"provider",
|
||||||
"network_availability_zone",
|
"network_availability_zone",
|
||||||
"dvr",
|
"dvr",
|
||||||
"router"]
|
"router",
|
||||||
|
"router_availability_zone"]
|
||||||
|
|
||||||
|
def __new__(cls, *args, **kwargs):
|
||||||
|
n = super(TricirclePlugin, cls).__new__(cls, *args, **kwargs)
|
||||||
|
registry.subscribe(n._set_distributed_flag,
|
||||||
|
resources.ROUTER, events.PRECOMMIT_CREATE)
|
||||||
|
return n
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(TricirclePlugin, self).__init__()
|
super(TricirclePlugin, self).__init__()
|
||||||
@ -160,12 +170,23 @@ class TricirclePlugin(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
# self.conn.create_consumer(self.topic, self.endpoints, fanout=False)
|
# self.conn.create_consumer(self.topic, self.endpoints, fanout=False)
|
||||||
# return self.conn.consume_in_threads()
|
# return self.conn.consume_in_threads()
|
||||||
|
|
||||||
|
def _set_distributed_flag(self, resource, event, trigger, context,
|
||||||
|
router, router_db, **kwargs):
|
||||||
|
"""Event handler to set distributed flag on creation."""
|
||||||
|
dist = l3_dvr_db.is_distributed_router(router)
|
||||||
|
router['distributed'] = dist
|
||||||
|
self.set_extra_attr_value(context, router_db, 'distributed', dist)
|
||||||
|
|
||||||
|
def validate_availability_zones(self, context, resource_type,
|
||||||
|
availability_zones):
|
||||||
|
self._validate_availability_zones(context, availability_zones)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _validate_availability_zones(context, az_list):
|
def _validate_availability_zones(context, az_list):
|
||||||
if not az_list:
|
if not az_list:
|
||||||
return
|
return
|
||||||
t_ctx = t_context.get_context_from_neutron_context(context)
|
t_ctx = t_context.get_context_from_neutron_context(context)
|
||||||
with context.session.begin():
|
with context.session.begin(subtransactions=True):
|
||||||
pods = core.query_resource(t_ctx, models.Pod, [], [])
|
pods = core.query_resource(t_ctx, models.Pod, [], [])
|
||||||
az_set = set(az_list)
|
az_set = set(az_list)
|
||||||
|
|
||||||
@ -368,16 +389,18 @@ class TricirclePlugin(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
def _convert_az2region_for_net(self, context, net):
|
def _convert_az2region_for_net(self, context, net):
|
||||||
az_hints = net.get(az_ext.AZ_HINTS)
|
az_hints = net.get(az_ext.AZ_HINTS)
|
||||||
if context.is_admin and az_hints:
|
if context.is_admin and az_hints:
|
||||||
net[az_ext.AZ_HINTS] = []
|
|
||||||
t_ctx = t_context.get_context_from_neutron_context(context)
|
t_ctx = t_context.get_context_from_neutron_context(context)
|
||||||
|
net[az_ext.AZ_HINTS] = self._convert_az2region(t_ctx, az_hints)
|
||||||
|
|
||||||
|
def _convert_az2region(self, t_ctx, az_hints):
|
||||||
|
region_names = set()
|
||||||
for az_hint in az_hints:
|
for az_hint in az_hints:
|
||||||
pods = db_api.find_pods_by_az_or_region(t_ctx, az_hint)
|
pods = db_api.find_pods_by_az_or_region(t_ctx, az_hint)
|
||||||
if not pods:
|
if not pods:
|
||||||
continue
|
continue
|
||||||
for pod in pods:
|
for pod in pods:
|
||||||
region_name = pod['region_name']
|
region_names.add(pod['region_name'])
|
||||||
if region_name not in net[az_ext.AZ_HINTS]:
|
return list(region_names)
|
||||||
net[az_ext.AZ_HINTS].append(region_name)
|
|
||||||
|
|
||||||
def get_network(self, context, network_id, fields=None):
|
def get_network(self, context, network_id, fields=None):
|
||||||
net = super(TricirclePlugin, self).get_network(context, network_id,
|
net = super(TricirclePlugin, self).get_network(context, network_id,
|
||||||
@ -1068,11 +1091,7 @@ class TricirclePlugin(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
with context.session.begin(subtransactions=True):
|
with context.session.begin(subtransactions=True):
|
||||||
router_db = super(TricirclePlugin, self).create_router(
|
router_db = super(TricirclePlugin, self).create_router(
|
||||||
context, router)
|
context, router)
|
||||||
router_db['extra_attributes'] = None
|
|
||||||
dist = l3_dvr_db.is_distributed_router(router['router'])
|
|
||||||
self.set_extra_attr_value(context, router_db, 'distributed', dist)
|
|
||||||
router_db['distributed'] = router_db[
|
|
||||||
'extra_attributes'].distributed
|
|
||||||
return router_db
|
return router_db
|
||||||
|
|
||||||
def _delete_top_bridge_resource(self, t_ctx, q_ctx, resource_type,
|
def _delete_top_bridge_resource(self, t_ctx, q_ctx, resource_type,
|
||||||
@ -1132,25 +1151,32 @@ class TricirclePlugin(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
t_ctx = t_context.get_context_from_neutron_context(context)
|
t_ctx = t_context.get_context_from_neutron_context(context)
|
||||||
mappings = db_api.get_bottom_mappings_by_top_id(t_ctx, _id,
|
mappings = db_api.get_bottom_mappings_by_top_id(t_ctx, _id,
|
||||||
t_constants.RT_ROUTER)
|
t_constants.RT_ROUTER)
|
||||||
|
is_local_router = self.helper.is_local_router(t_ctx, router)
|
||||||
for pod, b_router_id in mappings:
|
for pod, b_router_id in mappings:
|
||||||
b_client = self._get_client(pod['region_name'])
|
b_client = self._get_client(pod['region_name'])
|
||||||
|
if not is_local_router:
|
||||||
bridge_port_name = t_constants.bridge_port_name % (project_id,
|
bridge_port_name = t_constants.bridge_port_name % (project_id,
|
||||||
b_router_id)
|
b_router_id)
|
||||||
bridge_ports = super(TricirclePlugin, self).get_ports(
|
bridge_ports = super(TricirclePlugin, self).get_ports(
|
||||||
context, {'name': [bridge_port_name]})
|
context, {'name': [bridge_port_name]})
|
||||||
if bridge_ports:
|
if bridge_ports:
|
||||||
t_ns_port_id = bridge_ports[0]['id']
|
t_ns_port_id = bridge_ports[0]['id']
|
||||||
b_client.action_routers(t_ctx, 'remove_gateway', b_router_id)
|
b_client.action_routers(t_ctx, 'remove_gateway',
|
||||||
|
b_router_id)
|
||||||
self._delete_top_bridge_port(t_ctx, context, t_ns_port_id,
|
self._delete_top_bridge_port(t_ctx, context, t_ns_port_id,
|
||||||
bridge_port_name)
|
bridge_port_name)
|
||||||
b_client.delete_routers(t_ctx, b_router_id)
|
b_client.delete_routers(t_ctx, b_router_id)
|
||||||
db_api.delete_mappings_by_bottom_id(t_ctx, b_router_id)
|
db_api.delete_mappings_by_bottom_id(t_ctx, b_router_id)
|
||||||
|
|
||||||
|
if is_local_router:
|
||||||
|
super(TricirclePlugin, self).delete_router(context, _id)
|
||||||
|
return
|
||||||
|
|
||||||
mappings = db_api.get_bottom_mappings_by_top_id(
|
mappings = db_api.get_bottom_mappings_by_top_id(
|
||||||
t_ctx, _id, t_constants.RT_NS_ROUTER)
|
t_ctx, _id, t_constants.RT_NS_ROUTER)
|
||||||
for pod, b_ns_router_id in mappings:
|
for pod, b_ns_router_id in mappings:
|
||||||
b_client = self._get_client(pod['region_name'])
|
b_client = self._get_client(pod['region_name'])
|
||||||
bridge_subnet_name = t_constants.bridge_subnet_name % project_id
|
bridge_subnet_name = (t_constants.bridge_subnet_name % project_id)
|
||||||
bridge_subnets = super(TricirclePlugin,
|
bridge_subnets = super(TricirclePlugin,
|
||||||
self).get_subnets(
|
self).get_subnets(
|
||||||
context, {'name': [bridge_subnet_name]})
|
context, {'name': [bridge_subnet_name]})
|
||||||
@ -1267,7 +1293,7 @@ class TricirclePlugin(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
return self.helper.get_bottom_bridge_elements(
|
return self.helper.get_bottom_bridge_elements(
|
||||||
t_ctx, project_id, pod, t_net, is_external, t_subnet, t_port)
|
t_ctx, project_id, pod, t_net, is_external, t_subnet, t_port)
|
||||||
|
|
||||||
def _get_net_pods_by_interface_info(self, t_ctx, q_ctx, add_by_port,
|
def _get_net_id_by_interface_info(self, q_ctx, add_by_port,
|
||||||
interface_info):
|
interface_info):
|
||||||
if add_by_port:
|
if add_by_port:
|
||||||
port = self.get_port(q_ctx, interface_info['port_id'])
|
port = self.get_port(q_ctx, interface_info['port_id'])
|
||||||
@ -1275,6 +1301,12 @@ class TricirclePlugin(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
else:
|
else:
|
||||||
subnet = self.get_subnet(q_ctx, interface_info['subnet_id'])
|
subnet = self.get_subnet(q_ctx, interface_info['subnet_id'])
|
||||||
net_id = subnet['network_id']
|
net_id = subnet['network_id']
|
||||||
|
return net_id
|
||||||
|
|
||||||
|
def _get_net_pods_by_interface_info(self, t_ctx, q_ctx, add_by_port,
|
||||||
|
interface_info):
|
||||||
|
net_id = self._get_net_id_by_interface_info(q_ctx, add_by_port,
|
||||||
|
interface_info)
|
||||||
mappings = db_api.get_bottom_mappings_by_top_id(
|
mappings = db_api.get_bottom_mappings_by_top_id(
|
||||||
t_ctx, net_id, t_constants.RT_NETWORK)
|
t_ctx, net_id, t_constants.RT_NETWORK)
|
||||||
return net_id, [mapping[0] for mapping in mappings]
|
return net_id, [mapping[0] for mapping in mappings]
|
||||||
@ -1305,6 +1337,11 @@ class TricirclePlugin(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
# network ID from resource routing table.
|
# network ID from resource routing table.
|
||||||
if not network.get(az_ext.AZ_HINTS):
|
if not network.get(az_ext.AZ_HINTS):
|
||||||
raise t_exceptions.ExternalNetPodNotSpecify()
|
raise t_exceptions.ExternalNetPodNotSpecify()
|
||||||
|
|
||||||
|
t_router = self._get_router(context, router_id)
|
||||||
|
self.validate_router_net_location_match(t_ctx, t_router, network)
|
||||||
|
is_local_router = self.helper.is_local_router(t_ctx, t_router)
|
||||||
|
|
||||||
region_name = network[az_ext.AZ_HINTS][0]
|
region_name = network[az_ext.AZ_HINTS][0]
|
||||||
pod = db_api.get_pod_by_name(t_ctx, region_name)
|
pod = db_api.get_pod_by_name(t_ctx, region_name)
|
||||||
b_net_id = db_api.get_bottom_id_by_top_id_region_name(
|
b_net_id = db_api.get_bottom_id_by_top_id_region_name(
|
||||||
@ -1312,16 +1349,25 @@ class TricirclePlugin(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
|
|
||||||
# create corresponding bottom router in the pod where external network
|
# create corresponding bottom router in the pod where external network
|
||||||
# is located.
|
# is located.
|
||||||
t_router = self._get_router(context, router_id)
|
if is_local_router:
|
||||||
|
# if router is a local router, we will use RT_ROUTER to attach
|
||||||
|
# external network, else use RT_NS_ROUTER
|
||||||
|
router_type = t_constants.RT_ROUTER
|
||||||
|
is_distributed = t_router.get('distributed', False)
|
||||||
|
body = {'router': {'name': t_router['id'],
|
||||||
|
'distributed': is_distributed}}
|
||||||
|
|
||||||
|
else:
|
||||||
# TODO(zhiyuan) decide router is distributed or not from pod table
|
# TODO(zhiyuan) decide router is distributed or not from pod table
|
||||||
# currently "distributed" is set to False, should add a metadata field
|
# currently "distributed" is set to False, should add a metadata
|
||||||
# to pod table, and decide distributed or not from the metadata later
|
# field to pod table, and decide distributed or not from the
|
||||||
|
# metadata later
|
||||||
|
router_type = t_constants.RT_NS_ROUTER
|
||||||
body = {'router': {'name': t_constants.ns_router_name % router_id,
|
body = {'router': {'name': t_constants.ns_router_name % router_id,
|
||||||
'distributed': False}}
|
'distributed': False}}
|
||||||
|
|
||||||
_, b_router_id = self._prepare_bottom_element(
|
_, b_router_id = self._prepare_bottom_element(
|
||||||
t_ctx, t_router['tenant_id'], pod, t_router,
|
t_ctx, t_router['tenant_id'], pod, t_router, router_type, body)
|
||||||
t_constants.RT_NS_ROUTER, body)
|
|
||||||
|
|
||||||
# both router and external network in bottom pod are ready, attach
|
# both router and external network in bottom pod are ready, attach
|
||||||
# external network to router in bottom pod.
|
# external network to router in bottom pod.
|
||||||
@ -1342,6 +1388,9 @@ class TricirclePlugin(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
b_info['external_fixed_ips'] = fixed_ips
|
b_info['external_fixed_ips'] = fixed_ips
|
||||||
b_client.action_routers(t_ctx, 'add_gateway', b_router_id, b_info)
|
b_client.action_routers(t_ctx, 'add_gateway', b_router_id, b_info)
|
||||||
|
|
||||||
|
if is_local_router:
|
||||||
|
return
|
||||||
|
|
||||||
# when internal network(providing fixed ip) and external network
|
# when internal network(providing fixed ip) and external network
|
||||||
# (providing floating ip) are in different bottom pods, we utilize a
|
# (providing floating ip) are in different bottom pods, we utilize a
|
||||||
# bridge network to connect these two networks. here we create the
|
# bridge network to connect these two networks. here we create the
|
||||||
@ -1391,8 +1440,13 @@ class TricirclePlugin(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
raise t_exceptions.ExternalNetPodNotSpecify()
|
raise t_exceptions.ExternalNetPodNotSpecify()
|
||||||
|
|
||||||
region_name = t_network[az_ext.AZ_HINTS][0]
|
region_name = t_network[az_ext.AZ_HINTS][0]
|
||||||
|
is_local_router = self.helper.is_local_router(t_ctx, t_router)
|
||||||
|
|
||||||
b_router_id = db_api.get_bottom_id_by_top_id_region_name(
|
b_router_id = db_api.get_bottom_id_by_top_id_region_name(
|
||||||
t_ctx, router_id, region_name, t_constants.RT_NS_ROUTER)
|
t_ctx, router_id, region_name,
|
||||||
|
t_constants.RT_ROUTER if is_local_router
|
||||||
|
else t_constants.RT_NS_ROUTER)
|
||||||
|
|
||||||
b_client = self._get_client(region_name)
|
b_client = self._get_client(region_name)
|
||||||
b_client.action_routers(t_ctx, 'remove_gateway', b_router_id)
|
b_client.action_routers(t_ctx, 'remove_gateway', b_router_id)
|
||||||
|
|
||||||
@ -1430,22 +1484,55 @@ class TricirclePlugin(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
self._remove_router_gateway(context, router_id)
|
self._remove_router_gateway(context, router_id)
|
||||||
ret = super(TricirclePlugin, self).update_router(
|
ret = super(TricirclePlugin, self).update_router(
|
||||||
context, router_id, router)
|
context, router_id, router)
|
||||||
|
|
||||||
t_ctx = t_context.get_context_from_neutron_context(context)
|
t_ctx = t_context.get_context_from_neutron_context(context)
|
||||||
|
is_local_router = self.helper.is_local_router(t_ctx, router)
|
||||||
|
if not is_local_router:
|
||||||
self.xjob_handler.configure_extra_routes(t_ctx, router_id)
|
self.xjob_handler.configure_extra_routes(t_ctx, router_id)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
def validate_router_net_location_match(self, t_ctx, router, net):
|
||||||
|
router_az_hints = self.helper.get_router_az_hints(router)
|
||||||
|
if not router_az_hints:
|
||||||
|
return
|
||||||
|
router_region_names = self._convert_az2region(t_ctx, router_az_hints)
|
||||||
|
router_region_set = set(router_region_names)
|
||||||
|
|
||||||
|
net_az_hints = net.get(az_ext.AZ_HINTS)
|
||||||
|
if not net_az_hints:
|
||||||
|
raise t_exceptions.RouterNetworkLocationMismatch(
|
||||||
|
router_az_hints=router_region_names,
|
||||||
|
net_az_hints=['All Region'])
|
||||||
|
|
||||||
|
# net_az_hints already convert to region name when user is admin
|
||||||
|
if t_ctx.is_admin:
|
||||||
|
net_region_names = net_az_hints
|
||||||
|
else:
|
||||||
|
net_region_names = self._convert_az2region(t_ctx, net_az_hints)
|
||||||
|
net_region_set = set(net_region_names)
|
||||||
|
diff = net_region_set - router_region_set
|
||||||
|
if diff:
|
||||||
|
raise t_exceptions.RouterNetworkLocationMismatch(
|
||||||
|
router_az_hints=router_region_names,
|
||||||
|
net_az_hints=net_region_names)
|
||||||
|
|
||||||
def add_router_interface(self, context, router_id, interface_info):
|
def add_router_interface(self, context, router_id, interface_info):
|
||||||
t_ctx = t_context.get_context_from_neutron_context(context)
|
t_ctx = t_context.get_context_from_neutron_context(context)
|
||||||
|
|
||||||
router = self._get_router(context, router_id)
|
router = self._get_router(context, router_id)
|
||||||
project_id = router['tenant_id']
|
project_id = router['tenant_id']
|
||||||
add_by_port, _ = self._validate_interface_info(interface_info)
|
add_by_port, _ = self._validate_interface_info(interface_info)
|
||||||
|
net_id = self._get_net_id_by_interface_info(
|
||||||
|
context, add_by_port, interface_info)
|
||||||
|
net = self.get_network(context, net_id)
|
||||||
|
self.validate_router_net_location_match(t_ctx, router, net)
|
||||||
|
is_local_router = self.helper.is_local_router(t_ctx, router)
|
||||||
|
|
||||||
t_pod = db_api.get_top_pod(t_ctx)
|
t_pod = db_api.get_top_pod(t_ctx)
|
||||||
assert t_pod
|
assert t_pod
|
||||||
|
|
||||||
# bridge network for E-W and N-S networking
|
if not is_local_router:
|
||||||
|
# for single external network, need bridge network for
|
||||||
|
# E-W and N-S networking
|
||||||
pool_id = self._get_bridge_subnet_pool_id(
|
pool_id = self._get_bridge_subnet_pool_id(
|
||||||
t_ctx, context, None, t_pod)
|
t_ctx, context, None, t_pod)
|
||||||
self._get_bridge_network_subnet(
|
self._get_bridge_network_subnet(
|
||||||
@ -1453,7 +1540,7 @@ class TricirclePlugin(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
|
|
||||||
return_info = super(TricirclePlugin, self).add_router_interface(
|
return_info = super(TricirclePlugin, self).add_router_interface(
|
||||||
context, router_id, interface_info)
|
context, router_id, interface_info)
|
||||||
net_id, b_pods = self._get_net_pods_by_interface_info(
|
_, b_pods = self._get_net_pods_by_interface_info(
|
||||||
t_ctx, context, add_by_port, interface_info)
|
t_ctx, context, add_by_port, interface_info)
|
||||||
if not b_pods:
|
if not b_pods:
|
||||||
LOG.debug('Add router interface: no interfaces found, xjob not'
|
LOG.debug('Add router interface: no interfaces found, xjob not'
|
||||||
|
@ -18,6 +18,7 @@ import six
|
|||||||
|
|
||||||
from neutron_lib import constants
|
from neutron_lib import constants
|
||||||
import neutronclient.common.exceptions as q_cli_exceptions
|
import neutronclient.common.exceptions as q_cli_exceptions
|
||||||
|
from oslo_serialization import jsonutils
|
||||||
|
|
||||||
from tricircle.common import client
|
from tricircle.common import client
|
||||||
import tricircle.common.constants as t_constants
|
import tricircle.common.constants as t_constants
|
||||||
@ -642,3 +643,28 @@ class NetworkHelper(object):
|
|||||||
'port_range_max': rule.get('port_range_max'),
|
'port_range_max': rule.get('port_range_max'),
|
||||||
'port_range_min': rule.get('port_range_min'),
|
'port_range_min': rule.get('port_range_min'),
|
||||||
'security_group_id': sg_id}}
|
'security_group_id': sg_id}}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_router_az_hints(router):
|
||||||
|
# when called by api, availability_zone_hints included in
|
||||||
|
# extra_attributes, but when called by xjob, it included in router
|
||||||
|
# body directly.
|
||||||
|
extra_attributes = router.get('extra_attributes')
|
||||||
|
az_hints = router.get(AZ_HINTS)
|
||||||
|
if extra_attributes:
|
||||||
|
az_hints = extra_attributes.get(AZ_HINTS)
|
||||||
|
if not az_hints:
|
||||||
|
return None
|
||||||
|
if not isinstance(az_hints, list):
|
||||||
|
az_hints = jsonutils.loads(az_hints)
|
||||||
|
return az_hints
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def is_local_router(t_ctx, router):
|
||||||
|
router_az_hints = NetworkHelper.get_router_az_hints(router)
|
||||||
|
if not router_az_hints:
|
||||||
|
return False
|
||||||
|
if len(router_az_hints) > 1:
|
||||||
|
return False
|
||||||
|
router_az_hint = router_az_hints[0]
|
||||||
|
return bool(db_api.get_pod_by_name(t_ctx, router_az_hint))
|
||||||
|
@ -61,9 +61,8 @@ from oslo_utils import uuidutils
|
|||||||
from tricircle.common import client
|
from tricircle.common import client
|
||||||
from tricircle.common import constants
|
from tricircle.common import constants
|
||||||
from tricircle.common import context
|
from tricircle.common import context
|
||||||
|
from tricircle.common import exceptions as t_exceptions
|
||||||
from tricircle.common.i18n import _
|
from tricircle.common.i18n import _
|
||||||
|
|
||||||
import tricircle.db.api as db_api
|
import tricircle.db.api as db_api
|
||||||
from tricircle.db import core
|
from tricircle.db import core
|
||||||
from tricircle.db import models
|
from tricircle.db import models
|
||||||
@ -107,7 +106,8 @@ BOTTOM2_FIPS = []
|
|||||||
RES_LIST = [TOP_NETS, TOP_SUBNETS, TOP_PORTS, TOP_ROUTERS, TOP_ROUTERPORT,
|
RES_LIST = [TOP_NETS, TOP_SUBNETS, TOP_PORTS, TOP_ROUTERS, TOP_ROUTERPORT,
|
||||||
TOP_SUBNETPOOLS, TOP_SUBNETPOOLPREFIXES, TOP_IPALLOCATIONS,
|
TOP_SUBNETPOOLS, TOP_SUBNETPOOLPREFIXES, TOP_IPALLOCATIONS,
|
||||||
TOP_VLANALLOCATIONS, TOP_SEGMENTS, TOP_EXTNETS, TOP_FLOATINGIPS,
|
TOP_VLANALLOCATIONS, TOP_SEGMENTS, TOP_EXTNETS, TOP_FLOATINGIPS,
|
||||||
TOP_SGS, TOP_SG_RULES,
|
TOP_SGS, TOP_SG_RULES, TOP_NETWORK_RBAC, TOP_SUBNETROUTES,
|
||||||
|
TOP_DNSNAMESERVERS,
|
||||||
BOTTOM1_NETS, BOTTOM1_SUBNETS, BOTTOM1_PORTS, BOTTOM1_ROUTERS,
|
BOTTOM1_NETS, BOTTOM1_SUBNETS, BOTTOM1_PORTS, BOTTOM1_ROUTERS,
|
||||||
BOTTOM1_SGS, BOTTOM1_FIPS,
|
BOTTOM1_SGS, BOTTOM1_FIPS,
|
||||||
BOTTOM2_NETS, BOTTOM2_SUBNETS, BOTTOM2_PORTS, BOTTOM2_ROUTERS,
|
BOTTOM2_NETS, BOTTOM2_SUBNETS, BOTTOM2_PORTS, BOTTOM2_ROUTERS,
|
||||||
@ -251,6 +251,9 @@ class DotDict(dict):
|
|||||||
return []
|
return []
|
||||||
return self.get(item)
|
return self.get(item)
|
||||||
|
|
||||||
|
def to_dict(self):
|
||||||
|
return self
|
||||||
|
|
||||||
def __copy__(self):
|
def __copy__(self):
|
||||||
return DotDict(self)
|
return DotDict(self)
|
||||||
|
|
||||||
@ -572,6 +575,9 @@ class FakeClient(object):
|
|||||||
elif action == 'remove_interface':
|
elif action == 'remove_interface':
|
||||||
return self.remove_interface_routers(ctx, *args, **kwargs)
|
return self.remove_interface_routers(ctx, *args, **kwargs)
|
||||||
|
|
||||||
|
def _is_bridge_network_attached():
|
||||||
|
pass
|
||||||
|
|
||||||
def create_floatingips(self, ctx, body):
|
def create_floatingips(self, ctx, body):
|
||||||
fip = self.create_resources('floatingip', ctx, body)
|
fip = self.create_resources('floatingip', ctx, body)
|
||||||
for key in ['fixed_port_id']:
|
for key in ['fixed_port_id']:
|
||||||
@ -1856,20 +1862,24 @@ class PluginTest(unittest.TestCase,
|
|||||||
'resource_type': constants.RT_SUBNET})
|
'resource_type': constants.RT_SUBNET})
|
||||||
return t_net_id, t_subnet_id, b_net_id, b_subnet_id
|
return t_net_id, t_subnet_id, b_net_id, b_subnet_id
|
||||||
|
|
||||||
def _prepare_router_test(self, tenant_id, ctx, region_name, index):
|
def _prepare_router_test(self, tenant_id, ctx, region_name, index,
|
||||||
|
router_az_hints=None, net_az_hints=None,
|
||||||
|
create_new_router=False):
|
||||||
(t_net_id, t_subnet_id, b_net_id,
|
(t_net_id, t_subnet_id, b_net_id,
|
||||||
b_subnet_id) = self._prepare_network_test(tenant_id, ctx, region_name,
|
b_subnet_id) = self._prepare_network_test(
|
||||||
index)
|
tenant_id, ctx, region_name, index, az_hints=net_az_hints)
|
||||||
|
|
||||||
if len(TOP_ROUTERS) == 0:
|
|
||||||
t_router_id = uuidutils.generate_uuid()
|
t_router_id = uuidutils.generate_uuid()
|
||||||
t_router = {
|
t_router = {
|
||||||
'id': t_router_id,
|
'id': t_router_id,
|
||||||
'name': 'top_router',
|
'name': 'top_router',
|
||||||
'distributed': False,
|
'distributed': False,
|
||||||
'tenant_id': tenant_id,
|
'tenant_id': tenant_id,
|
||||||
'attached_ports': DotList()
|
'attached_ports': DotList(),
|
||||||
|
'extra_attributes': {
|
||||||
|
'availability_zone_hints': router_az_hints
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if create_new_router or len(TOP_ROUTERS) == 0:
|
||||||
TOP_ROUTERS.append(DotDict(t_router))
|
TOP_ROUTERS.append(DotDict(t_router))
|
||||||
else:
|
else:
|
||||||
t_router_id = TOP_ROUTERS[0]['id']
|
t_router_id = TOP_ROUTERS[0]['id']
|
||||||
@ -2262,6 +2272,196 @@ class PluginTest(unittest.TestCase,
|
|||||||
# each router interface adding will call add_gateway once
|
# each router interface adding will call add_gateway once
|
||||||
mock_action.assert_has_calls([call, call])
|
mock_action.assert_has_calls([call, call])
|
||||||
|
|
||||||
|
@patch.object(context, 'get_context_from_neutron_context')
|
||||||
|
def test_validation_router_net_location_match(self, mock_context):
|
||||||
|
self._basic_pod_route_setup()
|
||||||
|
pod4 = {'pod_id': 'pod_id_4',
|
||||||
|
'region_name': 'pod_4',
|
||||||
|
'az_name': 'az_name_2'}
|
||||||
|
db_api.create_pod(self.context, pod4)
|
||||||
|
|
||||||
|
fake_plugin = FakePlugin()
|
||||||
|
q_ctx = FakeNeutronContext()
|
||||||
|
t_ctx = context.get_db_context()
|
||||||
|
mock_context.return_value = t_ctx
|
||||||
|
tenant_id = TEST_TENANT_ID
|
||||||
|
|
||||||
|
router_az_hints = '["pod_1"]'
|
||||||
|
net_az_hints = '["pod_2"]'
|
||||||
|
(t_net_id, t_subnet_id,
|
||||||
|
t_router_id, b_net_id, b_subnet_id) = self._prepare_router_test(
|
||||||
|
tenant_id, t_ctx, 'pod_1', 1, router_az_hints, net_az_hints, True)
|
||||||
|
router = fake_plugin._get_router(q_ctx, t_router_id)
|
||||||
|
net = fake_plugin.get_network(q_ctx, t_net_id)
|
||||||
|
self.assertRaises(t_exceptions.RouterNetworkLocationMismatch,
|
||||||
|
fake_plugin.validate_router_net_location_match,
|
||||||
|
t_ctx, router, net)
|
||||||
|
|
||||||
|
router_az_hints = '["pod_1"]'
|
||||||
|
net_az_hints = '["pod_1", "az_name_2"]'
|
||||||
|
(t_net_id, t_subnet_id,
|
||||||
|
t_router_id, b_net_id, b_subnet_id) = self._prepare_router_test(
|
||||||
|
tenant_id, t_ctx, 'pod_1', 1, router_az_hints, net_az_hints, True)
|
||||||
|
router = fake_plugin._get_router(q_ctx, t_router_id)
|
||||||
|
net = fake_plugin.get_network(q_ctx, t_net_id)
|
||||||
|
self.assertRaises(t_exceptions.RouterNetworkLocationMismatch,
|
||||||
|
fake_plugin.validate_router_net_location_match,
|
||||||
|
t_ctx, router, net)
|
||||||
|
|
||||||
|
router_az_hints = '["az_name_1"]'
|
||||||
|
net_az_hints = '["az_name_1", "pod_2"]'
|
||||||
|
(t_net_id, t_subnet_id,
|
||||||
|
t_router_id, b_net_id, b_subnet_id) = self._prepare_router_test(
|
||||||
|
tenant_id, t_ctx, 'pod_1', 1, router_az_hints, net_az_hints, True)
|
||||||
|
router = fake_plugin._get_router(q_ctx, t_router_id)
|
||||||
|
net = fake_plugin.get_network(q_ctx, t_net_id)
|
||||||
|
self.assertRaises(t_exceptions.RouterNetworkLocationMismatch,
|
||||||
|
fake_plugin.validate_router_net_location_match,
|
||||||
|
t_ctx, router, net)
|
||||||
|
|
||||||
|
router_az_hints = '["pod_1"]'
|
||||||
|
net_az_hints = None
|
||||||
|
(t_net_id, t_subnet_id,
|
||||||
|
t_router_id, b_net_id, b_subnet_id) = self._prepare_router_test(
|
||||||
|
tenant_id, t_ctx, 'pod_1', 1, router_az_hints, net_az_hints, True)
|
||||||
|
router = fake_plugin._get_router(q_ctx, t_router_id)
|
||||||
|
net = fake_plugin.get_network(q_ctx, t_net_id)
|
||||||
|
self.assertRaises(t_exceptions.RouterNetworkLocationMismatch,
|
||||||
|
fake_plugin.validate_router_net_location_match,
|
||||||
|
t_ctx, router, net)
|
||||||
|
|
||||||
|
router_az_hints = None
|
||||||
|
net_az_hints = '["pod_1", "az_name_2"]'
|
||||||
|
(t_net_id, t_subnet_id,
|
||||||
|
t_router_id, b_net_id, b_subnet_id) = self._prepare_router_test(
|
||||||
|
tenant_id, t_ctx, 'pod_1', 1, router_az_hints, net_az_hints, True)
|
||||||
|
router = fake_plugin._get_router(q_ctx, t_router_id)
|
||||||
|
net = fake_plugin.get_network(q_ctx, t_net_id)
|
||||||
|
is_local_router = helper.NetworkHelper.is_local_router(t_ctx, router)
|
||||||
|
fake_plugin.validate_router_net_location_match(t_ctx, router, net)
|
||||||
|
self.assertEqual(is_local_router, False)
|
||||||
|
|
||||||
|
router_az_hints = None
|
||||||
|
net_az_hints = None
|
||||||
|
(t_net_id, t_subnet_id,
|
||||||
|
t_router_id, b_net_id, b_subnet_id) = self._prepare_router_test(
|
||||||
|
tenant_id, t_ctx, 'pod_1', 1, router_az_hints, net_az_hints, True)
|
||||||
|
router = fake_plugin._get_router(q_ctx, t_router_id)
|
||||||
|
net = fake_plugin.get_network(q_ctx, t_net_id)
|
||||||
|
is_local_router = helper.NetworkHelper.is_local_router(t_ctx, router)
|
||||||
|
fake_plugin.validate_router_net_location_match(t_ctx, router, net)
|
||||||
|
self.assertEqual(is_local_router, False)
|
||||||
|
|
||||||
|
router_az_hints = '["pod_1"]'
|
||||||
|
net_az_hints = '["pod_1"]'
|
||||||
|
(t_net_id, t_subnet_id,
|
||||||
|
t_router_id, b_net_id, b_subnet_id) = self._prepare_router_test(
|
||||||
|
tenant_id, t_ctx, 'pod_1', 1, router_az_hints, net_az_hints, True)
|
||||||
|
router = fake_plugin._get_router(q_ctx, t_router_id)
|
||||||
|
net = fake_plugin.get_network(q_ctx, t_net_id)
|
||||||
|
is_local_router = helper.NetworkHelper.is_local_router(t_ctx, router)
|
||||||
|
fake_plugin.validate_router_net_location_match(t_ctx, router, net)
|
||||||
|
self.assertEqual(is_local_router, True)
|
||||||
|
|
||||||
|
router_az_hints = '["az_name_2"]'
|
||||||
|
net_az_hints = '["az_name_2"]'
|
||||||
|
(t_net_id, t_subnet_id,
|
||||||
|
t_router_id, b_net_id, b_subnet_id) = self._prepare_router_test(
|
||||||
|
tenant_id, t_ctx, 'pod_1', 1, router_az_hints, net_az_hints, True)
|
||||||
|
router = fake_plugin._get_router(q_ctx, t_router_id)
|
||||||
|
net = fake_plugin.get_network(q_ctx, t_net_id)
|
||||||
|
is_local_router = helper.NetworkHelper.is_local_router(t_ctx, router)
|
||||||
|
fake_plugin.validate_router_net_location_match(t_ctx, router, net)
|
||||||
|
self.assertEqual(is_local_router, False)
|
||||||
|
|
||||||
|
router_az_hints = '["pod_1", "az_name_2"]'
|
||||||
|
net_az_hints = '["az_name_2"]'
|
||||||
|
t_ctx.is_admin = True
|
||||||
|
(t_net_id, t_subnet_id,
|
||||||
|
t_router_id, b_net_id, b_subnet_id) = self._prepare_router_test(
|
||||||
|
tenant_id, t_ctx, 'pod_1', 1, router_az_hints, net_az_hints, True)
|
||||||
|
router = fake_plugin._get_router(q_ctx, t_router_id)
|
||||||
|
net = fake_plugin.get_network(q_ctx, t_net_id)
|
||||||
|
is_local_router = helper.NetworkHelper.is_local_router(t_ctx, router)
|
||||||
|
fake_plugin.validate_router_net_location_match(t_ctx, router, net)
|
||||||
|
self.assertEqual(is_local_router, False)
|
||||||
|
|
||||||
|
net_az_hints = '["pod_1"]'
|
||||||
|
t_ctx.is_admin = True
|
||||||
|
(t_net_id, t_subnet_id, b_net_id,
|
||||||
|
b_subnet_id) = self._prepare_network_test(
|
||||||
|
tenant_id, t_ctx, 'pod_1', 1, az_hints=net_az_hints)
|
||||||
|
|
||||||
|
# add a use case: router's extra_attributes attr is not exist but
|
||||||
|
# availability_zone_hints attr exist
|
||||||
|
t_router = {
|
||||||
|
'id': uuidutils.generate_uuid(),
|
||||||
|
'name': 'top_router',
|
||||||
|
'distributed': False,
|
||||||
|
'tenant_id': tenant_id,
|
||||||
|
'attached_ports': DotList(),
|
||||||
|
'availability_zone_hints': ['pod_1']
|
||||||
|
}
|
||||||
|
|
||||||
|
net = fake_plugin.get_network(q_ctx, t_net_id)
|
||||||
|
is_local_router = helper.NetworkHelper.is_local_router(t_ctx, t_router)
|
||||||
|
fake_plugin.validate_router_net_location_match(t_ctx, router, net)
|
||||||
|
self.assertEqual(is_local_router, True)
|
||||||
|
|
||||||
|
@patch.object(directory, 'get_plugin', new=fake_get_plugin)
|
||||||
|
@patch.object(driver.Pool, 'get_instance', new=fake_get_instance)
|
||||||
|
@patch.object(ipam_pluggable_backend.IpamPluggableBackend,
|
||||||
|
'_allocate_ips_for_port', new=fake_allocate_ips_for_port)
|
||||||
|
@patch.object(db_base_plugin_common.DbBasePluginCommon,
|
||||||
|
'_make_subnet_dict', new=fake_make_subnet_dict)
|
||||||
|
@patch.object(FakePlugin, '_get_bridge_network_subnet')
|
||||||
|
@patch.object(FakeClient, 'add_gateway_routers')
|
||||||
|
@patch.object(FakeBaseRPCAPI, 'configure_extra_routes')
|
||||||
|
@patch.object(context, 'get_context_from_neutron_context')
|
||||||
|
def test_add_interface_for_local_router(
|
||||||
|
self, mock_context, mock_rpc, mock_action, mock_get_bridge_net):
|
||||||
|
self._basic_pod_route_setup()
|
||||||
|
|
||||||
|
fake_plugin = FakePlugin()
|
||||||
|
q_ctx = FakeNeutronContext()
|
||||||
|
t_ctx = context.get_db_context()
|
||||||
|
mock_context.return_value = t_ctx
|
||||||
|
tenant_id = TEST_TENANT_ID
|
||||||
|
|
||||||
|
router_az_hints = '["pod_1"]'
|
||||||
|
net_az_hints = '["pod_1"]'
|
||||||
|
(t_net_id, t_subnet_id,
|
||||||
|
t_router_id, b_net_id, b_subnet_id) = self._prepare_router_test(
|
||||||
|
tenant_id, t_ctx, 'pod_1', 1, router_az_hints, net_az_hints)
|
||||||
|
|
||||||
|
fake_plugin.add_router_interface(
|
||||||
|
q_ctx, t_router_id, {'subnet_id': t_subnet_id})['port_id']
|
||||||
|
|
||||||
|
_, b_router_id = db_api.get_bottom_mappings_by_top_id(
|
||||||
|
t_ctx, t_router_id, constants.RT_ROUTER)[0]
|
||||||
|
|
||||||
|
(t_net_id, t_subnet_id, t_router_id,
|
||||||
|
b_another_net_id, b_another_subnet_id) = self._prepare_router_test(
|
||||||
|
tenant_id, t_ctx, 'pod_1', 2, router_az_hints, net_az_hints)
|
||||||
|
|
||||||
|
fake_plugin.add_router_interface(
|
||||||
|
q_ctx, t_router_id, {'subnet_id': t_subnet_id})['port_id']
|
||||||
|
|
||||||
|
self.assertFalse(mock_rpc.called)
|
||||||
|
self.assertFalse(mock_action.called)
|
||||||
|
self.assertFalse(mock_get_bridge_net.called)
|
||||||
|
|
||||||
|
device_ids = ['', '']
|
||||||
|
for port in BOTTOM1_PORTS:
|
||||||
|
if (port['network_id'] == b_net_id) and (
|
||||||
|
port['device_owner'] == 'network:router_interface'):
|
||||||
|
device_ids[0] = port['device_id']
|
||||||
|
elif port['network_id'] == b_another_net_id and (
|
||||||
|
port['device_owner'] == 'network:router_interface'):
|
||||||
|
device_ids[1] = port['device_id']
|
||||||
|
|
||||||
|
self.assertEqual(device_ids, [b_router_id, b_router_id])
|
||||||
|
|
||||||
@patch.object(directory, 'get_plugin', new=fake_get_plugin)
|
@patch.object(directory, 'get_plugin', new=fake_get_plugin)
|
||||||
@patch.object(driver.Pool, 'get_instance', new=fake_get_instance)
|
@patch.object(driver.Pool, 'get_instance', new=fake_get_instance)
|
||||||
@patch.object(ipam_pluggable_backend.IpamPluggableBackend,
|
@patch.object(ipam_pluggable_backend.IpamPluggableBackend,
|
||||||
@ -2451,23 +2651,8 @@ class PluginTest(unittest.TestCase,
|
|||||||
t_ctx, top_net['id'], constants.RT_NETWORK)
|
t_ctx, top_net['id'], constants.RT_NETWORK)
|
||||||
self.assertEqual(mappings[0][1], bottom_net['id'])
|
self.assertEqual(mappings[0][1], bottom_net['id'])
|
||||||
|
|
||||||
@patch.object(directory, 'get_plugin', new=fake_get_plugin)
|
def _prepare_external_net_router_test(self, q_ctx, fake_plugin,
|
||||||
@patch.object(driver.Pool, 'get_instance', new=fake_get_instance)
|
router_az_hints=None):
|
||||||
@patch.object(ipam_pluggable_backend.IpamPluggableBackend,
|
|
||||||
'_allocate_ips_for_port', new=fake_allocate_ips_for_port)
|
|
||||||
@patch.object(l3_db.L3_NAT_dbonly_mixin, '_make_router_dict',
|
|
||||||
new=fake_make_router_dict)
|
|
||||||
@patch.object(db_base_plugin_common.DbBasePluginCommon,
|
|
||||||
'_make_subnet_dict', new=fake_make_subnet_dict)
|
|
||||||
@patch.object(FakeClient, 'action_routers')
|
|
||||||
@patch.object(context, 'get_context_from_neutron_context')
|
|
||||||
def test_set_gateway(self, mock_context, mock_action):
|
|
||||||
self._basic_pod_route_setup()
|
|
||||||
|
|
||||||
fake_plugin = FakePlugin()
|
|
||||||
q_ctx = FakeNeutronContext()
|
|
||||||
t_ctx = context.get_db_context()
|
|
||||||
mock_context.return_value = t_ctx
|
|
||||||
|
|
||||||
tenant_id = TEST_TENANT_ID
|
tenant_id = TEST_TENANT_ID
|
||||||
t_net_body = {
|
t_net_body = {
|
||||||
@ -2502,10 +2687,36 @@ class PluginTest(unittest.TestCase,
|
|||||||
'name': 'router',
|
'name': 'router',
|
||||||
'distributed': False,
|
'distributed': False,
|
||||||
'tenant_id': tenant_id,
|
'tenant_id': tenant_id,
|
||||||
'attached_ports': DotList()
|
'attached_ports': DotList(),
|
||||||
|
'extra_attributes': {
|
||||||
|
'availability_zone_hints': router_az_hints
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TOP_ROUTERS.append(DotDict(t_router))
|
TOP_ROUTERS.append(DotDict(t_router))
|
||||||
|
return t_net_id, t_subnet_id, t_router_id,
|
||||||
|
|
||||||
|
@patch.object(directory, 'get_plugin', new=fake_get_plugin)
|
||||||
|
@patch.object(driver.Pool, 'get_instance', new=fake_get_instance)
|
||||||
|
@patch.object(ipam_pluggable_backend.IpamPluggableBackend,
|
||||||
|
'_allocate_ips_for_port', new=fake_allocate_ips_for_port)
|
||||||
|
@patch.object(l3_db.L3_NAT_dbonly_mixin, '_make_router_dict',
|
||||||
|
new=fake_make_router_dict)
|
||||||
|
@patch.object(db_base_plugin_common.DbBasePluginCommon,
|
||||||
|
'_make_subnet_dict', new=fake_make_subnet_dict)
|
||||||
|
@patch.object(FakeClient, 'action_routers')
|
||||||
|
@patch.object(context, 'get_context_from_neutron_context')
|
||||||
|
def test_set_gateway(self, mock_context, mock_action):
|
||||||
|
self._basic_pod_route_setup()
|
||||||
|
|
||||||
|
fake_plugin = FakePlugin()
|
||||||
|
q_ctx = FakeNeutronContext()
|
||||||
|
t_ctx = context.get_db_context()
|
||||||
|
mock_context.return_value = t_ctx
|
||||||
|
|
||||||
|
t_net_id, t_subnet_id, t_router_id = (
|
||||||
|
self._prepare_external_net_router_test(q_ctx, fake_plugin))
|
||||||
|
|
||||||
fake_plugin.update_router(
|
fake_plugin.update_router(
|
||||||
q_ctx, t_router_id,
|
q_ctx, t_router_id,
|
||||||
{'router': {'external_gateway_info': {
|
{'router': {'external_gateway_info': {
|
||||||
@ -2534,6 +2745,54 @@ class PluginTest(unittest.TestCase,
|
|||||||
{'subnet_id': b_bridge_subnet_id})]
|
{'subnet_id': b_bridge_subnet_id})]
|
||||||
mock_action.assert_has_calls(calls)
|
mock_action.assert_has_calls(calls)
|
||||||
|
|
||||||
|
@patch.object(directory, 'get_plugin', new=fake_get_plugin)
|
||||||
|
@patch.object(driver.Pool, 'get_instance', new=fake_get_instance)
|
||||||
|
@patch.object(ipam_pluggable_backend.IpamPluggableBackend,
|
||||||
|
'_allocate_ips_for_port', new=fake_allocate_ips_for_port)
|
||||||
|
@patch.object(l3_db.L3_NAT_dbonly_mixin, '_make_router_dict',
|
||||||
|
new=fake_make_router_dict)
|
||||||
|
@patch.object(db_base_plugin_common.DbBasePluginCommon,
|
||||||
|
'_make_subnet_dict', new=fake_make_subnet_dict)
|
||||||
|
@patch.object(FakePlugin, '_get_bridge_network_subnet')
|
||||||
|
@patch.object(FakeClient, 'action_routers')
|
||||||
|
@patch.object(context, 'get_context_from_neutron_context')
|
||||||
|
def test_set_gateway_for_local_router(self, mock_context, mock_action,
|
||||||
|
mock_get_bridge_network):
|
||||||
|
self._basic_pod_route_setup()
|
||||||
|
|
||||||
|
fake_plugin = FakePlugin()
|
||||||
|
q_ctx = FakeNeutronContext()
|
||||||
|
t_ctx = context.get_db_context()
|
||||||
|
mock_context.return_value = t_ctx
|
||||||
|
|
||||||
|
router_az_hints = '["pod_1"]'
|
||||||
|
t_net_id, t_subnet_id, t_router_id = (
|
||||||
|
self._prepare_external_net_router_test(q_ctx, fake_plugin,
|
||||||
|
router_az_hints))
|
||||||
|
fake_plugin.update_router(
|
||||||
|
q_ctx, t_router_id,
|
||||||
|
{'router': {'external_gateway_info': {
|
||||||
|
'network_id': TOP_NETS[0]['id'],
|
||||||
|
'enable_snat': False,
|
||||||
|
'external_fixed_ips':
|
||||||
|
[{'subnet_id': t_subnet_id,
|
||||||
|
'ip_address': '100.64.0.5'}]}}})
|
||||||
|
|
||||||
|
b_router_id = BOTTOM1_ROUTERS[0]['id']
|
||||||
|
b_net_id = db_api.get_bottom_id_by_top_id_region_name(
|
||||||
|
t_ctx, t_net_id, 'pod_1', constants.RT_NETWORK)
|
||||||
|
b_subnet_id = db_api.get_bottom_id_by_top_id_region_name(
|
||||||
|
t_ctx, t_subnet_id, 'pod_1', constants.RT_SUBNET)
|
||||||
|
|
||||||
|
body = {'network_id': b_net_id,
|
||||||
|
'enable_snat': False,
|
||||||
|
'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)
|
||||||
|
self.assertFalse(mock_get_bridge_network.called)
|
||||||
|
|
||||||
@patch.object(directory, 'get_plugin', new=fake_get_plugin)
|
@patch.object(directory, 'get_plugin', new=fake_get_plugin)
|
||||||
@patch.object(driver.Pool, 'get_instance', new=fake_get_instance)
|
@patch.object(driver.Pool, 'get_instance', new=fake_get_instance)
|
||||||
@patch.object(ipam_pluggable_backend.IpamPluggableBackend,
|
@patch.object(ipam_pluggable_backend.IpamPluggableBackend,
|
||||||
@ -2606,20 +2865,24 @@ class PluginTest(unittest.TestCase,
|
|||||||
{'router': {'external_gateway_info': {}}})
|
{'router': {'external_gateway_info': {}}})
|
||||||
mock_action.assert_called_with(t_ctx, 'remove_gateway', b_ns_router_id)
|
mock_action.assert_called_with(t_ctx, 'remove_gateway', b_ns_router_id)
|
||||||
|
|
||||||
def _prepare_associate_floatingip_test(self, t_ctx, q_ctx, fake_plugin):
|
def _prepare_associate_floatingip_test(self, t_ctx, q_ctx, fake_plugin,
|
||||||
|
router_az_hints=None,
|
||||||
|
net_az_hints=None,
|
||||||
|
js_net_az_hints=None):
|
||||||
tenant_id = TEST_TENANT_ID
|
tenant_id = TEST_TENANT_ID
|
||||||
self._basic_pod_route_setup()
|
self._basic_pod_route_setup()
|
||||||
(t_net_id, t_subnet_id,
|
(t_net_id, t_subnet_id,
|
||||||
t_router_id, b_net_id, b_subnet_id) = self._prepare_router_test(
|
t_router_id, b_net_id, b_subnet_id) = self._prepare_router_test(
|
||||||
tenant_id, t_ctx, 'pod_1', 1)
|
tenant_id, t_ctx, 'pod_1', 1, router_az_hints, js_net_az_hints)
|
||||||
|
if not net_az_hints:
|
||||||
|
net_az_hints = ['pod_2']
|
||||||
net_body = {
|
net_body = {
|
||||||
'name': 'ext_net',
|
'name': 'ext_net',
|
||||||
'admin_state_up': True,
|
'admin_state_up': True,
|
||||||
'shared': False,
|
'shared': False,
|
||||||
'tenant_id': tenant_id,
|
'tenant_id': tenant_id,
|
||||||
'router:external': True,
|
'router:external': True,
|
||||||
'availability_zone_hints': ['pod_2']
|
'availability_zone_hints': net_az_hints
|
||||||
}
|
}
|
||||||
e_net = fake_plugin.create_network(q_ctx, {'network': net_body})
|
e_net = fake_plugin.create_network(q_ctx, {'network': net_body})
|
||||||
subnet_body = {
|
subnet_body = {
|
||||||
@ -2908,6 +3171,59 @@ class PluginTest(unittest.TestCase,
|
|||||||
self.assertEqual(top_res_nums[i] - top_pre_created_res_nums[i],
|
self.assertEqual(top_res_nums[i] - top_pre_created_res_nums[i],
|
||||||
len(top_res_set))
|
len(top_res_set))
|
||||||
|
|
||||||
|
@patch.object(directory, 'get_plugin', new=fake_get_plugin)
|
||||||
|
@patch.object(driver.Pool, 'get_instance', new=fake_get_instance)
|
||||||
|
@patch.object(ipam_pluggable_backend.IpamPluggableBackend,
|
||||||
|
'_allocate_ips_for_port', new=fake_allocate_ips_for_port)
|
||||||
|
@patch.object(l3_db.L3_NAT_dbonly_mixin, '_make_router_dict',
|
||||||
|
new=fake_make_router_dict)
|
||||||
|
@patch.object(db_base_plugin_common.DbBasePluginCommon,
|
||||||
|
'_make_subnet_dict', new=fake_make_subnet_dict)
|
||||||
|
@patch.object(l3_db.L3_NAT_dbonly_mixin, 'update_floatingip',
|
||||||
|
new=update_floatingip)
|
||||||
|
@patch.object(context, 'get_context_from_neutron_context')
|
||||||
|
def test_delete_local_router(self, mock_context):
|
||||||
|
fake_plugin = FakePlugin()
|
||||||
|
q_ctx = FakeNeutronContext()
|
||||||
|
t_ctx = context.get_db_context()
|
||||||
|
t_ctx.project_id = 'test_tenant_id'
|
||||||
|
mock_context.return_value = t_ctx
|
||||||
|
|
||||||
|
(t_port_id, b_port_id,
|
||||||
|
fip, e_net) = self._prepare_associate_floatingip_test(
|
||||||
|
t_ctx, q_ctx, fake_plugin, '["pod_1"]', ["pod_1"], '["pod_1"]')
|
||||||
|
# associate floating ip
|
||||||
|
fip_body = {'port_id': t_port_id}
|
||||||
|
fake_plugin.update_floatingip(q_ctx, fip['id'],
|
||||||
|
{'floatingip': fip_body})
|
||||||
|
# disassociate floating ip
|
||||||
|
fip_body = {'port_id': None}
|
||||||
|
fake_plugin.update_floatingip(q_ctx, fip['id'],
|
||||||
|
{'floatingip': fip_body})
|
||||||
|
|
||||||
|
t_router_id = TOP_ROUTERS[0]['id']
|
||||||
|
for port in TOP_PORTS:
|
||||||
|
if port['id'] == t_port_id:
|
||||||
|
t_subnet_id = port['fixed_ips'][0]['subnet_id']
|
||||||
|
fake_plugin.remove_router_interface(q_ctx, t_router_id,
|
||||||
|
{'subnet_id': t_subnet_id})
|
||||||
|
fake_plugin.update_router(q_ctx, t_router_id,
|
||||||
|
{'router': {'external_gateway_info': {}}})
|
||||||
|
|
||||||
|
top_res_sets = [TOP_NETS, TOP_SUBNETS, TOP_PORTS]
|
||||||
|
top_res_nums = [len(top_res_set) for top_res_set in top_res_sets]
|
||||||
|
top_pre_created_res_nums = [0, 0, 0]
|
||||||
|
for i, top_res_set in enumerate(top_res_sets):
|
||||||
|
for top_res in top_res_set:
|
||||||
|
if top_res.get('name', '').find('bridge') != -1:
|
||||||
|
top_pre_created_res_nums[i] += 1
|
||||||
|
fake_plugin.delete_router(q_ctx, t_router_id)
|
||||||
|
|
||||||
|
# check pre-created networks, subnets and ports are all deleted
|
||||||
|
for i, top_res_set in enumerate(top_res_sets):
|
||||||
|
self.assertEqual(top_res_nums[i] - top_pre_created_res_nums[i],
|
||||||
|
len(top_res_set))
|
||||||
|
|
||||||
@patch.object(context, 'get_context_from_neutron_context')
|
@patch.object(context, 'get_context_from_neutron_context')
|
||||||
def test_create_security_group_rule(self, mock_context):
|
def test_create_security_group_rule(self, mock_context):
|
||||||
self._basic_pod_route_setup()
|
self._basic_pod_route_setup()
|
||||||
|
@ -311,8 +311,10 @@ class XManager(PeriodicTasks):
|
|||||||
_, b_router_id = self.helper.prepare_bottom_element(
|
_, b_router_id = self.helper.prepare_bottom_element(
|
||||||
ctx, project_id, b_pod, t_router, constants.RT_ROUTER, router_body)
|
ctx, project_id, b_pod, t_router, constants.RT_ROUTER, router_body)
|
||||||
|
|
||||||
# create top bridge port
|
|
||||||
q_ctx = None # no need to pass neutron context when using client
|
q_ctx = None # no need to pass neutron context when using client
|
||||||
|
is_local_router = self.helper.is_local_router(ctx, t_router)
|
||||||
|
if not is_local_router:
|
||||||
|
# create top bridge port
|
||||||
t_bridge_port_id = self.helper.get_bridge_interface(
|
t_bridge_port_id = self.helper.get_bridge_interface(
|
||||||
ctx, q_ctx, project_id, t_pod, t_bridge_net['id'], b_router_id)
|
ctx, q_ctx, project_id, t_pod, t_bridge_net['id'], b_router_id)
|
||||||
|
|
||||||
@ -324,7 +326,8 @@ class XManager(PeriodicTasks):
|
|||||||
t_bridge_port = t_client.get_ports(ctx, t_bridge_port_id)
|
t_bridge_port = t_client.get_ports(ctx, t_bridge_port_id)
|
||||||
(is_new, b_bridge_port_id, b_bridge_subnet_id,
|
(is_new, b_bridge_port_id, b_bridge_subnet_id,
|
||||||
b_bridge_net_id) = self.helper.get_bottom_bridge_elements(
|
b_bridge_net_id) = self.helper.get_bottom_bridge_elements(
|
||||||
ctx, project_id, b_pod, t_bridge_net, True, t_bridge_subnet, None)
|
ctx, project_id, b_pod, t_bridge_net,
|
||||||
|
True, t_bridge_subnet, None)
|
||||||
|
|
||||||
# we attach the bridge port as router gateway
|
# we attach the bridge port as router gateway
|
||||||
# add_gateway is update operation, which can run multiple times
|
# add_gateway is update operation, which can run multiple times
|
||||||
@ -521,7 +524,11 @@ class XManager(PeriodicTasks):
|
|||||||
project_id = t_router['tenant_id']
|
project_id = t_router['tenant_id']
|
||||||
|
|
||||||
b_pod = db_api.get_pod(ctx, b_pod_id)
|
b_pod = db_api.get_pod(ctx, b_pod_id)
|
||||||
|
is_local_router = self.helper.is_local_router(ctx, t_router)
|
||||||
|
if is_local_router:
|
||||||
|
t_bridge_net = None
|
||||||
|
t_bridge_subnet = None
|
||||||
|
else:
|
||||||
t_bridge_net_name = constants.bridge_net_name % project_id
|
t_bridge_net_name = constants.bridge_net_name % project_id
|
||||||
t_bridge_subnet_name = constants.bridge_subnet_name % project_id
|
t_bridge_subnet_name = constants.bridge_subnet_name % project_id
|
||||||
t_bridge_net = self._get_resource_by_name(t_client, ctx, 'network',
|
t_bridge_net = self._get_resource_by_name(t_client, ctx, 'network',
|
||||||
@ -542,10 +549,11 @@ class XManager(PeriodicTasks):
|
|||||||
is_ext_net_pod = True
|
is_ext_net_pod = True
|
||||||
else:
|
else:
|
||||||
is_ext_net_pod = False
|
is_ext_net_pod = False
|
||||||
self._setup_router_one_pod(ctx, t_pod, b_pod, t_client, t_net,
|
|
||||||
t_router, t_bridge_net,
|
|
||||||
t_bridge_subnet, is_ext_net_pod)
|
|
||||||
|
|
||||||
|
self._setup_router_one_pod(
|
||||||
|
ctx, t_pod, b_pod, t_client, t_net, t_router, t_bridge_net,
|
||||||
|
t_bridge_subnet, is_ext_net_pod)
|
||||||
|
if not is_local_router:
|
||||||
self.xjob_handler.configure_extra_routes(ctx, t_router_id)
|
self.xjob_handler.configure_extra_routes(ctx, t_router_id)
|
||||||
|
|
||||||
@_job_handle(constants.JT_ROUTER)
|
@_job_handle(constants.JT_ROUTER)
|
||||||
|
Loading…
Reference in New Issue
Block a user