Add router_factory to l3-agent and L3 extension API
Currently, most implementations override the L3NatAgent class itself for their own logic since there is no proper interface to extend RouterInfo class. This adds unnecessary complexity for developers who just want to extend router mechanism instead of whole RPC. Add a RouterFactory class that developer can registers RouterInfo class and delegate it for RouterInfo creation. Seperate functions and variables which currently used externally to abstract class from RouterInfo, so that extension can use the basic interface. Provide the router registration function to the l3 extension API so that extension can extend RouterInfo itself which correspond to each features (ha, distribtued, ha + distributed) Depends-On: https://review.openstack.org/#/c/620348/ Closes-Bug: #1804634 Partially-Implements: blueprint openflow-based-dvr Change-Id: I1eff726900a8e67596814ca9a5f392938f154d7b
This commit is contained in:
parent
bb9edb25b0
commit
ec875b42b6
@ -195,6 +195,40 @@ class L3PluginApi(object):
|
|||||||
return cctxt.call(context, 'get_host_ha_router_count', host=self.host)
|
return cctxt.call(context, 'get_host_ha_router_count', host=self.host)
|
||||||
|
|
||||||
|
|
||||||
|
class RouterFactory(object):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._routers = {}
|
||||||
|
|
||||||
|
def register(self, features, router_cls):
|
||||||
|
"""Register router class which implements BaseRouterInfo
|
||||||
|
|
||||||
|
Features which is a list of strings converted to frozenset internally
|
||||||
|
for key uniqueness.
|
||||||
|
|
||||||
|
:param features: a list of strings of router's features
|
||||||
|
:param router_cls: a router class which implements BaseRouterInfo
|
||||||
|
"""
|
||||||
|
self._routers[frozenset(features)] = router_cls
|
||||||
|
|
||||||
|
def create(self, features, **kwargs):
|
||||||
|
"""Create router instance with registered router class
|
||||||
|
|
||||||
|
:param features: a list of strings of router's features
|
||||||
|
:param kwargs: arguments for router class
|
||||||
|
:returns: a router instance which implements BaseRouterInfo
|
||||||
|
:raises: n_exc.RouterNotFoundInRouterFactory
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
router = self._routers[frozenset(features)]
|
||||||
|
return router(**kwargs)
|
||||||
|
except KeyError:
|
||||||
|
exc = l3_exc.RouterNotFoundInRouterFactory(
|
||||||
|
router_id=kwargs['router_id'], features=features)
|
||||||
|
LOG.exception(exc.msg)
|
||||||
|
raise exc
|
||||||
|
|
||||||
|
|
||||||
@profiler.trace_cls("l3-agent")
|
@profiler.trace_cls("l3-agent")
|
||||||
class L3NATAgent(ha.AgentMixin,
|
class L3NATAgent(ha.AgentMixin,
|
||||||
dvr.AgentMixin,
|
dvr.AgentMixin,
|
||||||
@ -224,6 +258,8 @@ class L3NATAgent(ha.AgentMixin,
|
|||||||
else:
|
else:
|
||||||
self.conf = cfg.CONF
|
self.conf = cfg.CONF
|
||||||
self.router_info = {}
|
self.router_info = {}
|
||||||
|
self.router_factory = RouterFactory()
|
||||||
|
self._register_router_cls(self.router_factory)
|
||||||
|
|
||||||
self._check_config_params()
|
self._check_config_params()
|
||||||
|
|
||||||
@ -329,6 +365,21 @@ class L3NATAgent(ha.AgentMixin,
|
|||||||
except Exception:
|
except Exception:
|
||||||
LOG.exception('update_all_ha_network_port_statuses failed')
|
LOG.exception('update_all_ha_network_port_statuses failed')
|
||||||
|
|
||||||
|
def _register_router_cls(self, factory):
|
||||||
|
factory.register([], legacy_router.LegacyRouter)
|
||||||
|
factory.register(['ha'], ha_router.HaRouter)
|
||||||
|
|
||||||
|
if self.conf.agent_mode == lib_const.L3_AGENT_MODE_DVR_SNAT:
|
||||||
|
factory.register(['distributed'],
|
||||||
|
dvr_router.DvrEdgeRouter)
|
||||||
|
factory.register(['ha', 'distributed'],
|
||||||
|
dvr_edge_ha_router.DvrEdgeHaRouter)
|
||||||
|
else:
|
||||||
|
factory.register(['distributed'],
|
||||||
|
dvr_local_router.DvrLocalRouter)
|
||||||
|
factory.register(['ha', 'distributed'],
|
||||||
|
dvr_local_router.DvrLocalRouter)
|
||||||
|
|
||||||
def _check_config_params(self):
|
def _check_config_params(self):
|
||||||
"""Check items in configuration files.
|
"""Check items in configuration files.
|
||||||
|
|
||||||
@ -376,7 +427,6 @@ class L3NATAgent(ha.AgentMixin,
|
|||||||
raise Exception(msg)
|
raise Exception(msg)
|
||||||
|
|
||||||
def _create_router(self, router_id, router):
|
def _create_router(self, router_id, router):
|
||||||
args = []
|
|
||||||
kwargs = {
|
kwargs = {
|
||||||
'agent': self,
|
'agent': self,
|
||||||
'router_id': router_id,
|
'router_id': router_id,
|
||||||
@ -386,9 +436,15 @@ class L3NATAgent(ha.AgentMixin,
|
|||||||
'interface_driver': self.driver,
|
'interface_driver': self.driver,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
features = []
|
||||||
if router.get('distributed'):
|
if router.get('distributed'):
|
||||||
|
features.append('distributed')
|
||||||
kwargs['host'] = self.host
|
kwargs['host'] = self.host
|
||||||
|
|
||||||
|
if router.get('ha'):
|
||||||
|
features.append('ha')
|
||||||
|
kwargs['state_change_callback'] = self.enqueue_state_change
|
||||||
|
|
||||||
if router.get('distributed') and router.get('ha'):
|
if router.get('distributed') and router.get('ha'):
|
||||||
# Case 1: If the router contains information about the HA interface
|
# Case 1: If the router contains information about the HA interface
|
||||||
# and if the requesting agent is a DVR_SNAT agent then go ahead
|
# and if the requesting agent is a DVR_SNAT agent then go ahead
|
||||||
@ -399,22 +455,12 @@ class L3NATAgent(ha.AgentMixin,
|
|||||||
# that needs to provision a router namespace because of a DVR
|
# that needs to provision a router namespace because of a DVR
|
||||||
# service port (e.g. DHCP). So go ahead and create a regular DVR
|
# service port (e.g. DHCP). So go ahead and create a regular DVR
|
||||||
# edge router.
|
# edge router.
|
||||||
if (self.conf.agent_mode == lib_const.L3_AGENT_MODE_DVR_SNAT and
|
if (not router.get(lib_const.HA_INTERFACE_KEY) or
|
||||||
router.get(lib_const.HA_INTERFACE_KEY) is not None):
|
self.conf.agent_mode != lib_const.L3_AGENT_MODE_DVR_SNAT):
|
||||||
kwargs['state_change_callback'] = self.enqueue_state_change
|
features.remove('ha')
|
||||||
return dvr_edge_ha_router.DvrEdgeHaRouter(*args, **kwargs)
|
kwargs.pop('state_change_callback')
|
||||||
|
|
||||||
if router.get('distributed'):
|
return self.router_factory.create(features, **kwargs)
|
||||||
if self.conf.agent_mode == lib_const.L3_AGENT_MODE_DVR_SNAT:
|
|
||||||
return dvr_router.DvrEdgeRouter(*args, **kwargs)
|
|
||||||
else:
|
|
||||||
return dvr_local_router.DvrLocalRouter(*args, **kwargs)
|
|
||||||
|
|
||||||
if router.get('ha'):
|
|
||||||
kwargs['state_change_callback'] = self.enqueue_state_change
|
|
||||||
return ha_router.HaRouter(*args, **kwargs)
|
|
||||||
|
|
||||||
return legacy_router.LegacyRouter(*args, **kwargs)
|
|
||||||
|
|
||||||
@lockutils.synchronized('resize_greenpool')
|
@lockutils.synchronized('resize_greenpool')
|
||||||
def _resize_process_pool(self):
|
def _resize_process_pool(self):
|
||||||
@ -487,7 +533,8 @@ class L3NATAgent(ha.AgentMixin,
|
|||||||
|
|
||||||
def init_extension_manager(self, connection):
|
def init_extension_manager(self, connection):
|
||||||
l3_ext_manager.register_opts(self.conf)
|
l3_ext_manager.register_opts(self.conf)
|
||||||
self.agent_api = l3_ext_api.L3AgentExtensionAPI(self.router_info)
|
self.agent_api = l3_ext_api.L3AgentExtensionAPI(self.router_info,
|
||||||
|
self.router_factory)
|
||||||
self.l3_ext_manager = (
|
self.l3_ext_manager = (
|
||||||
l3_ext_manager.L3AgentExtensionsManager(self.conf))
|
l3_ext_manager.L3AgentExtensionsManager(self.conf))
|
||||||
self.l3_ext_manager.initialize(
|
self.l3_ext_manager.initialize(
|
||||||
|
@ -26,8 +26,9 @@ class L3AgentExtensionAPI(object):
|
|||||||
agent's RouterInfo object.
|
agent's RouterInfo object.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def __init__(self, router_info):
|
def __init__(self, router_info, router_factory):
|
||||||
self._router_info = router_info
|
self._router_info = router_info
|
||||||
|
self._router_factory = router_factory
|
||||||
|
|
||||||
def _local_namespaces(self):
|
def _local_namespaces(self):
|
||||||
local_ns_list = ip_lib.list_network_namespaces()
|
local_ns_list = ip_lib.list_network_namespaces()
|
||||||
@ -68,3 +69,9 @@ class L3AgentExtensionAPI(object):
|
|||||||
def get_router_info(self, router_id):
|
def get_router_info(self, router_id):
|
||||||
"""Return RouterInfo for the given router id."""
|
"""Return RouterInfo for the given router id."""
|
||||||
return self._router_info.get(router_id)
|
return self._router_info.get(router_id)
|
||||||
|
|
||||||
|
def register_router(self, features, router_cls):
|
||||||
|
"""Register router class with the given features. This is for the
|
||||||
|
plugin to ovrride with their own ``router_info`` class.
|
||||||
|
"""
|
||||||
|
self._router_factory.register(features, router_cls)
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import abc
|
||||||
import collections
|
import collections
|
||||||
|
|
||||||
import netaddr
|
import netaddr
|
||||||
@ -19,6 +20,7 @@ from neutron_lib import constants as lib_constants
|
|||||||
from neutron_lib.exceptions import l3 as l3_exc
|
from neutron_lib.exceptions import l3 as l3_exc
|
||||||
from neutron_lib.utils import helpers
|
from neutron_lib.utils import helpers
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
import six
|
||||||
|
|
||||||
from neutron._i18n import _
|
from neutron._i18n import _
|
||||||
from neutron.agent.l3 import namespaces
|
from neutron.agent.l3 import namespaces
|
||||||
@ -41,7 +43,8 @@ ADDRESS_SCOPE_MARK_ID_MAX = 2048
|
|||||||
DEFAULT_ADDRESS_SCOPE = "noscope"
|
DEFAULT_ADDRESS_SCOPE = "noscope"
|
||||||
|
|
||||||
|
|
||||||
class RouterInfo(object):
|
@six.add_metaclass(abc.ABCMeta)
|
||||||
|
class BaseRouterInfo(object):
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
agent,
|
agent,
|
||||||
@ -52,16 +55,88 @@ class RouterInfo(object):
|
|||||||
use_ipv6=False):
|
use_ipv6=False):
|
||||||
self.agent = agent
|
self.agent = agent
|
||||||
self.router_id = router_id
|
self.router_id = router_id
|
||||||
self.agent_conf = agent_conf
|
# Invoke the setter for establishing initial SNAT action
|
||||||
self.ex_gw_port = None
|
|
||||||
self._snat_enabled = None
|
self._snat_enabled = None
|
||||||
self.fip_map = {}
|
self.router = router
|
||||||
|
self.agent_conf = agent_conf
|
||||||
|
self.driver = interface_driver
|
||||||
|
self.use_ipv6 = use_ipv6
|
||||||
|
|
||||||
self.internal_ports = []
|
self.internal_ports = []
|
||||||
|
self.ns_name = None
|
||||||
|
self.process_monitor = None
|
||||||
|
|
||||||
|
def initialize(self, process_monitor):
|
||||||
|
"""Initialize the router on the system.
|
||||||
|
|
||||||
|
This differs from __init__ in that this method actually affects the
|
||||||
|
system creating namespaces, starting processes, etc. The other merely
|
||||||
|
initializes the python object. This separates in-memory object
|
||||||
|
initialization from methods that actually go do stuff to the system.
|
||||||
|
|
||||||
|
:param process_monitor: The agent's process monitor instance.
|
||||||
|
"""
|
||||||
|
self.process_monitor = process_monitor
|
||||||
|
|
||||||
|
@property
|
||||||
|
def router(self):
|
||||||
|
return self._router
|
||||||
|
|
||||||
|
@router.setter
|
||||||
|
def router(self, value):
|
||||||
|
self._router = value
|
||||||
|
if not self._router:
|
||||||
|
return
|
||||||
|
# enable_snat by default if it wasn't specified by plugin
|
||||||
|
self._snat_enabled = self._router.get('enable_snat', True)
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def delete(self, agent):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def process(self, agent):
|
||||||
|
"""Process updates to this router
|
||||||
|
|
||||||
|
This method is the point where the agent requests that updates be
|
||||||
|
applied to this router.
|
||||||
|
|
||||||
|
:param agent: Passes the agent in order to send RPC messages.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_ex_gw_port(self):
|
||||||
|
return self.router.get('gw_port')
|
||||||
|
|
||||||
|
def get_gw_ns_name(self):
|
||||||
|
return self.ns_name
|
||||||
|
|
||||||
|
def get_internal_device_name(self, port_id):
|
||||||
|
return (INTERNAL_DEV_PREFIX + port_id)[:self.driver.DEV_NAME_LEN]
|
||||||
|
|
||||||
|
def get_external_device_name(self, port_id):
|
||||||
|
return (EXTERNAL_DEV_PREFIX + port_id)[:self.driver.DEV_NAME_LEN]
|
||||||
|
|
||||||
|
def get_external_device_interface_name(self, ex_gw_port):
|
||||||
|
return self.get_external_device_name(ex_gw_port['id'])
|
||||||
|
|
||||||
|
|
||||||
|
class RouterInfo(BaseRouterInfo):
|
||||||
|
|
||||||
|
def __init__(self,
|
||||||
|
agent,
|
||||||
|
router_id,
|
||||||
|
router,
|
||||||
|
agent_conf,
|
||||||
|
interface_driver,
|
||||||
|
use_ipv6=False):
|
||||||
|
super(RouterInfo, self).__init__(agent, router_id, router, agent_conf,
|
||||||
|
interface_driver, use_ipv6)
|
||||||
|
|
||||||
|
self.ex_gw_port = None
|
||||||
|
self.fip_map = {}
|
||||||
self.pd_subnets = {}
|
self.pd_subnets = {}
|
||||||
self.floating_ips = set()
|
self.floating_ips = set()
|
||||||
# Invoke the setter for establishing initial SNAT action
|
|
||||||
self.router = router
|
|
||||||
self.use_ipv6 = use_ipv6
|
|
||||||
ns = self.create_router_namespace_object(
|
ns = self.create_router_namespace_object(
|
||||||
router_id, agent_conf, interface_driver, use_ipv6)
|
router_id, agent_conf, interface_driver, use_ipv6)
|
||||||
self.router_namespace = ns
|
self.router_namespace = ns
|
||||||
@ -76,8 +151,6 @@ class RouterInfo(object):
|
|||||||
self.initialize_address_scope_iptables()
|
self.initialize_address_scope_iptables()
|
||||||
self.initialize_metadata_iptables()
|
self.initialize_metadata_iptables()
|
||||||
self.routes = []
|
self.routes = []
|
||||||
self.driver = interface_driver
|
|
||||||
self.process_monitor = None
|
|
||||||
# radvd is a neutron.agent.linux.ra.DaemonMonitor
|
# radvd is a neutron.agent.linux.ra.DaemonMonitor
|
||||||
self.radvd = None
|
self.radvd = None
|
||||||
self.centralized_port_forwarding_fip_set = set()
|
self.centralized_port_forwarding_fip_set = set()
|
||||||
@ -85,16 +158,7 @@ class RouterInfo(object):
|
|||||||
self.qos_gateway_ips = set()
|
self.qos_gateway_ips = set()
|
||||||
|
|
||||||
def initialize(self, process_monitor):
|
def initialize(self, process_monitor):
|
||||||
"""Initialize the router on the system.
|
super(RouterInfo, self).initialize(process_monitor)
|
||||||
|
|
||||||
This differs from __init__ in that this method actually affects the
|
|
||||||
system creating namespaces, starting processes, etc. The other merely
|
|
||||||
initializes the python object. This separates in-memory object
|
|
||||||
initialization from methods that actually go do stuff to the system.
|
|
||||||
|
|
||||||
:param process_monitor: The agent's process monitor instance.
|
|
||||||
"""
|
|
||||||
self.process_monitor = process_monitor
|
|
||||||
self.radvd = ra.DaemonMonitor(self.router_id,
|
self.radvd = ra.DaemonMonitor(self.router_id,
|
||||||
self.ns_name,
|
self.ns_name,
|
||||||
process_monitor,
|
process_monitor,
|
||||||
@ -108,33 +172,9 @@ class RouterInfo(object):
|
|||||||
return namespaces.RouterNamespace(
|
return namespaces.RouterNamespace(
|
||||||
router_id, agent_conf, iface_driver, use_ipv6)
|
router_id, agent_conf, iface_driver, use_ipv6)
|
||||||
|
|
||||||
@property
|
|
||||||
def router(self):
|
|
||||||
return self._router
|
|
||||||
|
|
||||||
@router.setter
|
|
||||||
def router(self, value):
|
|
||||||
self._router = value
|
|
||||||
if not self._router:
|
|
||||||
return
|
|
||||||
# enable_snat by default if it wasn't specified by plugin
|
|
||||||
self._snat_enabled = self._router.get('enable_snat', True)
|
|
||||||
|
|
||||||
def is_router_master(self):
|
def is_router_master(self):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def get_internal_device_name(self, port_id):
|
|
||||||
return (INTERNAL_DEV_PREFIX + port_id)[:self.driver.DEV_NAME_LEN]
|
|
||||||
|
|
||||||
def get_external_device_name(self, port_id):
|
|
||||||
return (EXTERNAL_DEV_PREFIX + port_id)[:self.driver.DEV_NAME_LEN]
|
|
||||||
|
|
||||||
def get_external_device_interface_name(self, ex_gw_port):
|
|
||||||
return self.get_external_device_name(ex_gw_port['id'])
|
|
||||||
|
|
||||||
def get_gw_ns_name(self):
|
|
||||||
return self.ns_name
|
|
||||||
|
|
||||||
def _update_routing_table(self, operation, route, namespace):
|
def _update_routing_table(self, operation, route, namespace):
|
||||||
cmd = ['ip', 'route', operation, 'to', route['destination'],
|
cmd = ['ip', 'route', operation, 'to', route['destination'],
|
||||||
'via', route['nexthop']]
|
'via', route['nexthop']]
|
||||||
@ -159,9 +199,6 @@ class RouterInfo(object):
|
|||||||
LOG.debug("Removed route entry is '%s'", route)
|
LOG.debug("Removed route entry is '%s'", route)
|
||||||
self.update_routing_table('delete', route)
|
self.update_routing_table('delete', route)
|
||||||
|
|
||||||
def get_ex_gw_port(self):
|
|
||||||
return self.router.get('gw_port')
|
|
||||||
|
|
||||||
def get_floating_ips(self):
|
def get_floating_ips(self):
|
||||||
"""Filter Floating IPs to be hosted on this agent."""
|
"""Filter Floating IPs to be hosted on this agent."""
|
||||||
return self.router.get(lib_constants.FLOATINGIP_KEY, [])
|
return self.router.get(lib_constants.FLOATINGIP_KEY, [])
|
||||||
@ -1170,13 +1207,6 @@ class RouterInfo(object):
|
|||||||
|
|
||||||
@common_utils.exception_logger()
|
@common_utils.exception_logger()
|
||||||
def process(self):
|
def process(self):
|
||||||
"""Process updates to this router
|
|
||||||
|
|
||||||
This method is the point where the agent requests that updates be
|
|
||||||
applied to this router.
|
|
||||||
|
|
||||||
:param agent: Passes the agent in order to send RPC messages.
|
|
||||||
"""
|
|
||||||
LOG.debug("Process updates, router %s", self.router['id'])
|
LOG.debug("Process updates, router %s", self.router['id'])
|
||||||
self.centralized_port_forwarding_fip_set = set(self.router.get(
|
self.centralized_port_forwarding_fip_set = set(self.router.get(
|
||||||
'port_forwardings_fip_set', set()))
|
'port_forwardings_fip_set', set()))
|
||||||
|
@ -579,6 +579,10 @@ class TestDvrRouter(framework.L3AgentTestFramework):
|
|||||||
|
|
||||||
self._add_fip_agent_gw_port_info_to_router(router,
|
self._add_fip_agent_gw_port_info_to_router(router,
|
||||||
external_gw_port)
|
external_gw_port)
|
||||||
|
# Router creation is delegated to router_factory. We have to
|
||||||
|
# re-register here so that factory can find override agent mode
|
||||||
|
# normally.
|
||||||
|
self.agent._register_router_cls(self.agent.router_factory)
|
||||||
return router
|
return router
|
||||||
|
|
||||||
def _get_fip_agent_gw_port_for_router(
|
def _get_fip_agent_gw_port_for_router(
|
||||||
|
@ -128,7 +128,7 @@ class QosExtensionBaseTestCase(test_agent.BasicRouterOperationsFramework):
|
|||||||
'L3AgentExtensionAPI.get_router_info').start()
|
'L3AgentExtensionAPI.get_router_info').start()
|
||||||
self.get_router_info.side_effect = _mock_get_router_info
|
self.get_router_info.side_effect = _mock_get_router_info
|
||||||
|
|
||||||
self.agent_api = l3_ext_api.L3AgentExtensionAPI(None)
|
self.agent_api = l3_ext_api.L3AgentExtensionAPI(None, None)
|
||||||
self.fip_qos_ext.consume_api(self.agent_api)
|
self.fip_qos_ext.consume_api(self.agent_api)
|
||||||
|
|
||||||
|
|
||||||
|
@ -129,7 +129,7 @@ class QosExtensionBaseTestCase(test_agent.BasicRouterOperationsFramework):
|
|||||||
'L3AgentExtensionAPI.get_router_info').start()
|
'L3AgentExtensionAPI.get_router_info').start()
|
||||||
self.get_router_info.side_effect = _mock_get_router_info
|
self.get_router_info.side_effect = _mock_get_router_info
|
||||||
|
|
||||||
self.agent_api = l3_ext_api.L3AgentExtensionAPI(None)
|
self.agent_api = l3_ext_api.L3AgentExtensionAPI(None, None)
|
||||||
self.gw_ip_qos_ext.consume_api(self.agent_api)
|
self.gw_ip_qos_ext.consume_api(self.agent_api)
|
||||||
|
|
||||||
|
|
||||||
|
@ -91,7 +91,7 @@ class PortForwardingExtensionBaseTestCase(
|
|||||||
'L3AgentExtensionAPI.get_router_info').start()
|
'L3AgentExtensionAPI.get_router_info').start()
|
||||||
self.get_router_info.return_value = self.router_info
|
self.get_router_info.return_value = self.router_info
|
||||||
|
|
||||||
self.agent_api = l3_ext_api.L3AgentExtensionAPI(None)
|
self.agent_api = l3_ext_api.L3AgentExtensionAPI(None, None)
|
||||||
self.fip_pf_ext.consume_api(self.agent_api)
|
self.fip_pf_ext.consume_api(self.agent_api)
|
||||||
|
|
||||||
self.port_forwardings = [self.portforwarding1]
|
self.port_forwardings = [self.portforwarding1]
|
||||||
|
@ -34,7 +34,9 @@ from testtools import matchers
|
|||||||
|
|
||||||
from neutron.agent.common import resource_processing_queue
|
from neutron.agent.common import resource_processing_queue
|
||||||
from neutron.agent.l3 import agent as l3_agent
|
from neutron.agent.l3 import agent as l3_agent
|
||||||
|
from neutron.agent.l3 import dvr_edge_ha_router
|
||||||
from neutron.agent.l3 import dvr_edge_router as dvr_router
|
from neutron.agent.l3 import dvr_edge_router as dvr_router
|
||||||
|
from neutron.agent.l3 import dvr_local_router
|
||||||
from neutron.agent.l3 import dvr_router_base
|
from neutron.agent.l3 import dvr_router_base
|
||||||
from neutron.agent.l3 import dvr_snat_ns
|
from neutron.agent.l3 import dvr_snat_ns
|
||||||
from neutron.agent.l3 import ha_router
|
from neutron.agent.l3 import ha_router
|
||||||
@ -428,6 +430,67 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
|
|||||||
self.assertEqual(len(stale_router_ids), destroy_proxy.call_count)
|
self.assertEqual(len(stale_router_ids), destroy_proxy.call_count)
|
||||||
destroy_proxy.assert_has_calls(expected_calls, any_order=True)
|
destroy_proxy.assert_has_calls(expected_calls, any_order=True)
|
||||||
|
|
||||||
|
def test__create_router_legacy_agent(self):
|
||||||
|
router = {'distributed': False, 'ha': False}
|
||||||
|
|
||||||
|
agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
|
||||||
|
router_info = agent._create_router(_uuid(), router)
|
||||||
|
|
||||||
|
self.assertEqual(legacy_router.LegacyRouter, type(router_info))
|
||||||
|
|
||||||
|
def test__create_router_ha_agent(self):
|
||||||
|
router = {'distributed': False, 'ha': True}
|
||||||
|
|
||||||
|
agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
|
||||||
|
router_info = agent._create_router(_uuid(), router)
|
||||||
|
|
||||||
|
self.assertEqual(ha_router.HaRouter, type(router_info))
|
||||||
|
|
||||||
|
def test__create_router_dvr_agent(self):
|
||||||
|
router = {'distributed': True, 'ha': False}
|
||||||
|
|
||||||
|
agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
|
||||||
|
router_info = agent._create_router(_uuid(), router)
|
||||||
|
|
||||||
|
self.assertEqual(dvr_local_router.DvrLocalRouter, type(router_info))
|
||||||
|
|
||||||
|
def test__create_router_dvr_agent_with_dvr_snat_mode(self):
|
||||||
|
router = {'distributed': True, 'ha': False}
|
||||||
|
|
||||||
|
self.conf.set_override('agent_mode',
|
||||||
|
lib_constants.L3_AGENT_MODE_DVR_SNAT)
|
||||||
|
agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
|
||||||
|
router_info = agent._create_router(_uuid(), router)
|
||||||
|
|
||||||
|
self.assertEqual(dvr_router.DvrEdgeRouter, type(router_info))
|
||||||
|
|
||||||
|
def test__create_router_dvr_ha_agent(self):
|
||||||
|
router = {'distributed': True, 'ha': True}
|
||||||
|
|
||||||
|
agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
|
||||||
|
router_info = agent._create_router(_uuid(), router)
|
||||||
|
|
||||||
|
self.assertEqual(dvr_local_router.DvrLocalRouter, type(router_info))
|
||||||
|
|
||||||
|
def test__create_router_dvr_ha_agent_with_dvr_snat_mode(self):
|
||||||
|
router = {'distributed': True, 'ha': True,
|
||||||
|
lib_constants.HA_INTERFACE_KEY: None}
|
||||||
|
|
||||||
|
self.conf.set_override('agent_mode',
|
||||||
|
lib_constants.L3_AGENT_MODE_DVR_SNAT)
|
||||||
|
agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
|
||||||
|
router_info = agent._create_router(_uuid(), router)
|
||||||
|
|
||||||
|
self.assertEqual(dvr_router.DvrEdgeRouter, type(router_info))
|
||||||
|
|
||||||
|
router = {'distributed': True, 'ha': True,
|
||||||
|
lib_constants.HA_INTERFACE_KEY: True}
|
||||||
|
|
||||||
|
agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
|
||||||
|
router_info = agent._create_router(_uuid(), router)
|
||||||
|
|
||||||
|
self.assertEqual(dvr_edge_ha_router.DvrEdgeHaRouter, type(router_info))
|
||||||
|
|
||||||
def test_router_info_create(self):
|
def test_router_info_create(self):
|
||||||
id = _uuid()
|
id = _uuid()
|
||||||
agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
|
agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
|
||||||
|
@ -18,8 +18,9 @@ import mock
|
|||||||
|
|
||||||
from oslo_utils import uuidutils
|
from oslo_utils import uuidutils
|
||||||
|
|
||||||
|
from neutron.agent.l3 import agent
|
||||||
from neutron.agent.l3 import l3_agent_extension_api as l3_agent_api
|
from neutron.agent.l3 import l3_agent_extension_api as l3_agent_api
|
||||||
from neutron.agent.l3 import router_info
|
from neutron.agent.l3 import router_info as l3router
|
||||||
from neutron.agent.linux import ip_lib
|
from neutron.agent.linux import ip_lib
|
||||||
from neutron.conf.agent import common as config
|
from neutron.conf.agent import common as config
|
||||||
from neutron.conf.agent.l3 import config as l3_config
|
from neutron.conf.agent.l3 import config as l3_config
|
||||||
@ -38,7 +39,7 @@ class TestL3AgentExtensionApi(base.BaseTestCase):
|
|||||||
'agent_conf': self.conf,
|
'agent_conf': self.conf,
|
||||||
'interface_driver': mock.ANY,
|
'interface_driver': mock.ANY,
|
||||||
'use_ipv6': mock.ANY}
|
'use_ipv6': mock.ANY}
|
||||||
ri = router_info.RouterInfo(mock.Mock(), self.router_id, **ri_kwargs)
|
ri = l3router.RouterInfo(mock.Mock(), self.router_id, **ri_kwargs)
|
||||||
ri.internal_ports = ports
|
ri.internal_ports = ports
|
||||||
return {ri.router_id: ri}, ri
|
return {ri.router_id: ri}, ri
|
||||||
|
|
||||||
@ -51,7 +52,7 @@ class TestL3AgentExtensionApi(base.BaseTestCase):
|
|||||||
'list_network_namespaces') as mock_list_netns:
|
'list_network_namespaces') as mock_list_netns:
|
||||||
|
|
||||||
mock_list_netns.return_value = []
|
mock_list_netns.return_value = []
|
||||||
api_object = l3_agent_api.L3AgentExtensionAPI(router_info)
|
api_object = l3_agent_api.L3AgentExtensionAPI(router_info, None)
|
||||||
router = api_object.get_router_hosting_port(port_ids[0])
|
router = api_object.get_router_hosting_port(port_ids[0])
|
||||||
|
|
||||||
mock_list_netns.assert_called_once_with()
|
mock_list_netns.assert_called_once_with()
|
||||||
@ -65,7 +66,7 @@ class TestL3AgentExtensionApi(base.BaseTestCase):
|
|||||||
with mock.patch.object(ip_lib,
|
with mock.patch.object(ip_lib,
|
||||||
'list_network_namespaces') as mock_list_netns:
|
'list_network_namespaces') as mock_list_netns:
|
||||||
mock_list_netns.return_value = [ri.ns_name]
|
mock_list_netns.return_value = [ri.ns_name]
|
||||||
api_object = l3_agent_api.L3AgentExtensionAPI(router_info)
|
api_object = l3_agent_api.L3AgentExtensionAPI(router_info, None)
|
||||||
router = api_object.get_router_hosting_port(port_ids[0])
|
router = api_object.get_router_hosting_port(port_ids[0])
|
||||||
self.assertEqual(ri, router)
|
self.assertEqual(ri, router)
|
||||||
|
|
||||||
@ -75,7 +76,7 @@ class TestL3AgentExtensionApi(base.BaseTestCase):
|
|||||||
with mock.patch.object(ip_lib,
|
with mock.patch.object(ip_lib,
|
||||||
'list_network_namespaces') as mock_list_netns:
|
'list_network_namespaces') as mock_list_netns:
|
||||||
mock_list_netns.return_value = [ri.ns_name]
|
mock_list_netns.return_value = [ri.ns_name]
|
||||||
api_object = l3_agent_api.L3AgentExtensionAPI(router_info)
|
api_object = l3_agent_api.L3AgentExtensionAPI(router_info, None)
|
||||||
routers = api_object.get_routers_in_project(self.project_id)
|
routers = api_object.get_routers_in_project(self.project_id)
|
||||||
self.assertEqual([ri], routers)
|
self.assertEqual([ri], routers)
|
||||||
|
|
||||||
@ -85,7 +86,7 @@ class TestL3AgentExtensionApi(base.BaseTestCase):
|
|||||||
with mock.patch.object(ip_lib,
|
with mock.patch.object(ip_lib,
|
||||||
'list_network_namespaces') as mock_list_netns:
|
'list_network_namespaces') as mock_list_netns:
|
||||||
mock_list_netns.return_value = [ri.ns_name]
|
mock_list_netns.return_value = [ri.ns_name]
|
||||||
api_object = l3_agent_api.L3AgentExtensionAPI(router_info)
|
api_object = l3_agent_api.L3AgentExtensionAPI(router_info, None)
|
||||||
router_in_ns = api_object.is_router_in_namespace(ri.router_id)
|
router_in_ns = api_object.is_router_in_namespace(ri.router_id)
|
||||||
self.assertTrue(router_in_ns)
|
self.assertTrue(router_in_ns)
|
||||||
|
|
||||||
@ -95,17 +96,32 @@ class TestL3AgentExtensionApi(base.BaseTestCase):
|
|||||||
with mock.patch.object(ip_lib,
|
with mock.patch.object(ip_lib,
|
||||||
'list_network_namespaces') as mock_list_netns:
|
'list_network_namespaces') as mock_list_netns:
|
||||||
mock_list_netns.return_value = [uuidutils.generate_uuid()]
|
mock_list_netns.return_value = [uuidutils.generate_uuid()]
|
||||||
api_object = l3_agent_api.L3AgentExtensionAPI(router_info)
|
api_object = l3_agent_api.L3AgentExtensionAPI(router_info, None)
|
||||||
router_in_ns = api_object.is_router_in_namespace(ri.router_id)
|
router_in_ns = api_object.is_router_in_namespace(ri.router_id)
|
||||||
self.assertFalse(router_in_ns)
|
self.assertFalse(router_in_ns)
|
||||||
|
|
||||||
def test_get_router_info(self):
|
def test_get_router_info(self):
|
||||||
router_info, ri = self._prepare_router_data()
|
router_info, ri = self._prepare_router_data()
|
||||||
api_object = l3_agent_api.L3AgentExtensionAPI(router_info)
|
api_object = l3_agent_api.L3AgentExtensionAPI(router_info, None)
|
||||||
self.assertEqual(ri, api_object.get_router_info(self.router_id))
|
self.assertEqual(ri, api_object.get_router_info(self.router_id))
|
||||||
|
|
||||||
def test_get_router_info_nonexistent(self):
|
def test_get_router_info_nonexistent(self):
|
||||||
router_info, ri = self._prepare_router_data()
|
router_info, ri = self._prepare_router_data()
|
||||||
api_object = l3_agent_api.L3AgentExtensionAPI(router_info)
|
api_object = l3_agent_api.L3AgentExtensionAPI(router_info, None)
|
||||||
self.assertIsNone(
|
self.assertIsNone(
|
||||||
api_object.get_router_info(uuidutils.generate_uuid()))
|
api_object.get_router_info(uuidutils.generate_uuid()))
|
||||||
|
|
||||||
|
def test_register_router(self):
|
||||||
|
router_info, ri = self._prepare_router_data()
|
||||||
|
router_info_cls = l3router.BaseRouterInfo
|
||||||
|
router_factory = agent.RouterFactory()
|
||||||
|
api_object = l3_agent_api.L3AgentExtensionAPI(router_info,
|
||||||
|
router_factory)
|
||||||
|
self.assertIsNone(
|
||||||
|
api_object.register_router([], router_info_cls))
|
||||||
|
self.assertIsNone(
|
||||||
|
api_object.register_router(['ha'], router_info_cls))
|
||||||
|
self.assertIsNone(
|
||||||
|
api_object.register_router(['distributed'], router_info_cls))
|
||||||
|
self.assertIsNone(
|
||||||
|
api_object.register_router(['ha', 'distributed'], router_info_cls))
|
||||||
|
@ -75,7 +75,7 @@ class L3LoggingExtBaseTestCase(test_agent.BasicRouterOperationsFramework):
|
|||||||
'neutron.agent.l3.l3_agent_extension_api.'
|
'neutron.agent.l3.l3_agent_extension_api.'
|
||||||
'L3AgentExtensionAPI.get_router_info').start()
|
'L3AgentExtensionAPI.get_router_info').start()
|
||||||
self.get_router_info.side_effect = _mock_get_router_info
|
self.get_router_info.side_effect = _mock_get_router_info
|
||||||
self.agent_api = l3_ext_api.L3AgentExtensionAPI(None)
|
self.agent_api = l3_ext_api.L3AgentExtensionAPI(None, None)
|
||||||
mock.patch(
|
mock.patch(
|
||||||
'neutron.manager.NeutronManager.load_class_for_provider').start()
|
'neutron.manager.NeutronManager.load_class_for_provider').start()
|
||||||
|
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
A new parameter ``router_factory`` has been added to
|
||||||
|
``neutron.agent.l3.L3AgentExtensionAPI``. Developers can register
|
||||||
|
``neutron.agent.l3.agent.RouterInfo`` class and delegate it for
|
||||||
|
``RouterInfo`` creation.
|
||||||
|
|
||||||
|
Extensions can extend ``RouterInfo`` itself which correspond to each
|
||||||
|
features (ha, distribtued, ha + distributed).
|
Loading…
Reference in New Issue
Block a user