L3 mixin to plugin

Details regarding theL3 mixin to plugin blueprint:

Scope
Refactor current implementation so that L3 routing functionality (i.e., routing/NAT/floatingip) can be provided by a separate service plugin. Traditional Neutron plugins will still be able to provide the L3 functionality integrated as today for those that prefer that.

This blueprint does not migrate L3 API into core, it will still be an extension. It does not provide a new L3 routing implementation, and essentially leaves the L3 agent untouched (only a minor change).

Use cases
When L3 functionality is provided as separate plugin, multiple such implementations can made available, just as there exists a wide variety of traditional Neutron plugins.

The cloud provider can decide at deployment time which combination of L2 and L3 implementations/strategies to use by making appropriate configuration file settings to load the wanted plugins.

Implementation overview
The router:external attribute part of the existing l3 extension is broken out into its own ‘external-net’ extension. The functions associated with this attributed are migrated to an ‘Ext_net_db_mixin’ class but are otherwise untouched. This means that the only change to plugins is to add the ‘extnet’ extension to the list of extensions they implement and inherit the ‘Ext_net_db_mixin’.

Plugins that continue to provide the L3 routing functionality integrated, will keep the ‘router’ extension as well as keep inheriting the ‘L3_NAT_db_mixin' (and their RPCCallback class will keep inheriting the 'L3RpcCallbackMixin').

Those plugins that delegate L3 routing functionality to a separate L3 router service plugin will remove the ‘router’ extension and the L3 mixin classes. The 'delete_port(…)' function of the plugin must be slightly modified to use a reference to the l3 plugin for the port check and floatingip cleanup.

The 'L3_NAT_db_mixin' is changed so that calls to functions in the 'NeutronDbPluginV2' class uses a reference to the core plugin instead of ‘self’. This way, the calls will work both when the 'L3_NAT_db_mixin' is inherited by the same plugin class that inherits 'NeutronDbPluginV2' and when this is not the case, i.e., with a separate L3 router plugin.

A new service type constant ‘L3_ROUTER_NAT’ is added to /neutron/plugins/common/constants.py.

A new ‘L3RouterPlugin’ class implementing the L3 router service plugin using the L3 extension (with 'L3_NAT_db_mixin' and 'L3RpcCallbackMixin') is introduced. The service type that the new service plugin reports is ‘L3_ROUTER_NAT’.

The plugin loading in 'NeutronManager' is slightly modified so that a plugin for service type L3_ROUTER_NAT is correctly added to the 'service_plugins' list. If a traditional plugin implementing the L3 extension and the new L3 router service plugin are both specified to be used the loading step will throw an exception.

How the name of the extended attribute 'router:external' is kept
Problem: To enable L3 routing as a service plugin instead of integrated in the core plugins as today the external network attribute must be moved from the L3 extension to a separate (Ext_net) extension.

XML serialization/deserialization fails when the external network attribute extension is moved from the L3 extension (with alias "router" to the new Ext_net extension (with alias "extnet").

Reason: The attribute is named "router:external", i.e., it contains the alias of the L3 extension. This alias is then used in the XML serialization to add an XML namespace to ensure the attribute name is unique.

Now, when the attribute is no longer provided by the L3 extension but instead by the new Ext_net extension XML serialization fails. More specifically, the required XML namespace is not added to the XML encoded string. The XML serialization then throws an exception. This is essentially since there will be a mismatch between alias and namespace of the extension providing the attribute and the alias used in the attribute name.

Solution: I've have added the ability for an extension to specify "backward compatibility" mappings between aliases and namespaces (it's a dict) for situations like this. That mapping is used in XML (de-/)serialization. Extensions provide this mapping by implementing a new function called "get_alias_namespace_compatibility_map". The function has a default implementation in the class "extensions.ExtensionDescriptor" where it returns an empty dictionary:

def get_alias_namespace_compatibility_map(self): return {} The above solution allows the external network attribute to keep its name "router:external" while being provided by the new Ext_net extension with alias "extnet" and the L3 extension keeps its alias "router".

Data model changes
None.

Configuration variables
No new configurations variables for the server side (the plugin). The L3 service plugin is specified using the ‘service_plugins’ setting in ‘quantum.conf’ in accordance with the service insertion framework.

For the L3 agent a new True/False configuration variable, 'separate_l3_plugin', is added. It should be set to 'True' when the L3 routing service plugin is used, 'False' otherwise. This configuration is needed since the L3 service plugin uses a different topic for RPC than the traditional Neutron plugins. Hence, the L3 agent must know which topic to use and it determines that based on the value of 'separate_l3_plugin'.

API's
No new APIs.

Plugin interface
No, but comments are made in the code about possible enhancements.

These concerns port usage, how plugins can reference each other and state dependency.

Required Plugin support
For a traditional plugin to "delegate" to the separate L3 routing service plugins the following steps are required:

For example,
 * Do not inherit 'l3_db.L3_NAT_db_mixin' anymore.
 * Remove "router" from 'supported_extension_aliases'.
 * Modify any 'self' references to members in L3_NAT_db_mixin to instead use 'manager.NeutronManager.get_service_plugins[constants.L3_ROUTER_NAT]'.

self.prevent_l3_port_deletion(...)

becomes something like

plugin = manager.QuantumManager.get_service_plugins.get(         constants.L3_ROUTER_NAT) if plugin: plugin.prevent_l3_port_deletion(...)


 * Do not inherit 'l3_rpc_base.L3RpcCallbackMixin' in any '*RpcCallbacks' class.

 

Dependencies:
No new dependencies.

CLI Requirements:
None.

Usage Example:
The code patch for this blueprint contains a L3 router service plugin that provides the L3 routing functionality that is otherwise provided by the traditional plugins. The Openvswitch plugin has been modified so that it does not provide L3 functionality. For this plugin the L3 routing functionality is instead delegated to the L3 routing service plugin.

The L3 plugin is specified by the ‘service_plugins’ setting in ‘quantum.conf’ and the l3 agent is configured in the usual way in the ‘l3agent.ini’ file.

Excerpt from quantum.conf:

core_plugin = neutron.plugins.openvswitch.ovs_neutron_plugin.OVSNeutronPluginV2 service_plugins = neutron.services.l3_router.l3_router_plugin.L3RouterPlugin

Test Cases:
Unit tests will be extended to also cover the case where the L3 routing functionality is handled by a routing service plugin separate from the traditional plugin. Tests for the external network attribute are put in a separate file for that extension.