Metaplugin decomposition
The main code of Metaplugin is available here: https://github.com/ntt-sic/networking-metaplugin Change-Id: I9bf6609df4bbe5ed679fb42156f8dea4c87303c6 Partially-implements: blueprint core-vendor-decomposition Closes-bug: #1415304
This commit is contained in:
parent
0680e0b524
commit
6b991396f4
@ -401,7 +401,7 @@ The following chart captures the following aspects:
|
|||||||
+-------------------------------+-----------------------+-----------+------------------+---------+--------------+
|
+-------------------------------+-----------------------+-----------+------------------+---------+--------------+
|
||||||
| networking-hyperv_ | | | | | |
|
| networking-hyperv_ | | | | | |
|
||||||
+-------------------------------+-----------------------+-----------+------------------+---------+--------------+
|
+-------------------------------+-----------------------+-----------+------------------+---------+--------------+
|
||||||
| networking-metaplugin_ | | | | | |
|
| networking-metaplugin_ | core | no | no | [C] | Kilo |
|
||||||
+-------------------------------+-----------------------+-----------+------------------+---------+--------------+
|
+-------------------------------+-----------------------+-----------+------------------+---------+--------------+
|
||||||
| networking-midonet_ | | | | | |
|
| networking-midonet_ | | | | | |
|
||||||
+-------------------------------+-----------------------+-----------+------------------+---------+--------------+
|
+-------------------------------+-----------------------+-----------+------------------+---------+--------------+
|
||||||
@ -440,6 +440,11 @@ Arista
|
|||||||
|
|
||||||
.. _networking-metaplugin:
|
.. _networking-metaplugin:
|
||||||
|
|
||||||
|
Metaplugin
|
||||||
|
----------
|
||||||
|
|
||||||
|
* Git: https://github.com/ntt-sic/networking-metaplugin
|
||||||
|
|
||||||
.. _networking-midonet:
|
.. _networking-midonet:
|
||||||
|
|
||||||
.. _networking-mlnx:
|
.. _networking-mlnx:
|
||||||
|
@ -1,92 +1,6 @@
|
|||||||
# -- Background
|
# NOTE
|
||||||
|
|
||||||
This plugin supports multiple plugin at same time. This plugin is for L3 connectivility
|
|
||||||
between networks which are realized by different plugins.This plugin adds new attributes 'flavor:network' and 'flavor:router".
|
|
||||||
flavor:network corresponds to specific l2 plugin ( flavor-plugin mapping could be configurable by plugin_list config.
|
|
||||||
flavor:router corresponds to specific l3 plugin ( flavor-plugin mapping could be configurable by l3_plugin_list config. Note that Metaplugin can provide l3 functionaliteis for l2 plugin which didn't support l3 extension yet.
|
|
||||||
This plugin also support extensions. We can map extension to plugin by using extension_map config.
|
|
||||||
|
|
||||||
[database]
|
|
||||||
# This line MUST be changed to actually run the plugin.
|
|
||||||
# Example:
|
|
||||||
# connection = mysql://root:nova@127.0.0.1:3306/ovs_neutron
|
|
||||||
# Replace 127.0.0.1 above with the IP address of the database used by the
|
|
||||||
# main neutron server. (Leave it as is if the database runs on this host.)
|
|
||||||
connection = mysql://root:password@localhost/neutron_metaplugin?charset=utf8
|
|
||||||
|
|
||||||
# Database reconnection retry times - in event connectivity is lost
|
|
||||||
# set to -1 implgies an infinite retry count
|
|
||||||
# max_retries = 10
|
|
||||||
# Database reconnection interval in seconds - in event connectivity is lost
|
|
||||||
retry_interval = 2
|
|
||||||
|
|
||||||
[meta]
|
|
||||||
## This is list of flavor:neutron_plugins
|
|
||||||
# extension method is used in the order of this list
|
|
||||||
plugin_list= 'openvswitch:neutron.plugins.openvswitch.ovs_neutron_plugin.OVSneutronPluginV2,linuxbridge:neutron.plugins.linuxbridge.lb_neutron_plugin.LinuxBridgePluginV2'
|
|
||||||
# plugin for l3
|
|
||||||
l3_plugin_list= 'openvswitch:neutron.plugins.openvswitch.ovs_neutron_plugin.OVSneutronPluginV2,linuxbridge:neutron.plugins.linuxbridge.lb_neutron_plugin.LinuxBridgePluginV2'
|
|
||||||
|
|
||||||
# Default value of flavor
|
|
||||||
default_flavor = 'openvswitch'
|
|
||||||
# Default value for l3
|
|
||||||
default_l3_flavor = 'openvswitch'
|
|
||||||
|
|
||||||
# supported extensions
|
|
||||||
supported_extension_aliases = 'providernet'
|
|
||||||
# specific method map for each flavor to extensions
|
|
||||||
extension_map = 'get_port_stats:nvp'
|
|
||||||
|
|
||||||
# -- BridgeDriver Configration
|
|
||||||
# In order to use metaplugin, you should use MetaDriver. Following configation is needed.
|
|
||||||
|
|
||||||
[DEFAULT]
|
|
||||||
# Meta Plugin
|
|
||||||
# Mapping between flavor and driver
|
|
||||||
meta_flavor_driver_mappings = openvswitch:neutron.agent.linux.interface.OVSInterfaceDriver, linuxbridge:neutron.agent.linux.interface.BridgeInterfaceDriver
|
|
||||||
# interface driver for MetaPlugin
|
|
||||||
interface_driver = neutron.agent.linux.interface.MetaInterfaceDriver
|
|
||||||
|
|
||||||
[proxy]
|
|
||||||
auth_url = http://10.0.0.1:35357/v2.0
|
|
||||||
auth_region = RegionOne
|
|
||||||
admin_tenant_name = service
|
|
||||||
admin_user = neutron
|
|
||||||
admin_password = password
|
|
||||||
|
|
||||||
|
|
||||||
# -- Agent
|
|
||||||
Agents for Metaplugin are in neutron/plugins/metaplugin/agent
|
|
||||||
linuxbridge_neutron_agent and ovs_neutron_agent is available.
|
|
||||||
|
|
||||||
# -- Extensions
|
|
||||||
|
|
||||||
- flavor
|
|
||||||
MetaPlugin supports flavor and provider net extension.
|
|
||||||
Metaplugin select plugin_list using flavor.
|
|
||||||
One plugin may use multiple flavor value. If the plugin support flavor, it may provide
|
|
||||||
multiple flavor of network.
|
|
||||||
|
|
||||||
- Attribute extension
|
|
||||||
Each plugin can use attribute extension such as provider_net, if you specify that in supported_extension_aliases.
|
|
||||||
|
|
||||||
- providernet
|
|
||||||
Vlan ID range of each plugin should be different, since Metaplugin dose not manage that.
|
|
||||||
|
|
||||||
#- limitations
|
|
||||||
|
|
||||||
Basically, All plugin should inherit NeutronDbPluginV2.
|
|
||||||
Metaplugin assumes all plugin share same Database especially for IPAM part in NeutronV2 API.
|
|
||||||
You can use another plugin if you use ProxyPluginV2, which proxies request to the another neutron server.
|
|
||||||
|
|
||||||
Example flavor configration for ProxyPluginV2
|
|
||||||
|
|
||||||
meta_flavor_driver_mappings = "openvswitch:neutron.agent.linux.interface.OVSInterfaceDriver,proxy:neutron.plugins.metaplugin.proxy_neutron_plugin.ProxyPluginV2"
|
|
||||||
|
|
||||||
- Limited L3 support
|
|
||||||
In folsom version, l3 is an extension. There is no way to extend exntension attributes.
|
|
||||||
so you can set flavor:router value but you can't get flavor:router value in API output.
|
|
||||||
L3 agent dont's support flavor:router.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
The main source codes of Metaplugin is now in https://github.com/ntt-sic/networking-metaplugin.
|
||||||
|
They were moved from Neutron tree to there according to core-vendor-decomposition.
|
||||||
|
Defining config and DB are still here according to the decomposition policy.
|
||||||
|
Codes of 'flavor' extension and interface driver used by *-agent remain in Neutron tree too.
|
||||||
|
@ -1,50 +0,0 @@
|
|||||||
# Copyright 2012, Nachi Ueno, NTT MCL, Inc.
|
|
||||||
# All Rights Reserved.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
from sqlalchemy.orm import exc
|
|
||||||
|
|
||||||
from neutron.plugins.metaplugin import meta_models_v2
|
|
||||||
|
|
||||||
|
|
||||||
def get_flavor_by_network(session, net_id):
|
|
||||||
try:
|
|
||||||
binding = (session.query(meta_models_v2.NetworkFlavor).
|
|
||||||
filter_by(network_id=net_id).
|
|
||||||
one())
|
|
||||||
except exc.NoResultFound:
|
|
||||||
return None
|
|
||||||
return binding.flavor
|
|
||||||
|
|
||||||
|
|
||||||
def add_network_flavor_binding(session, flavor, net_id):
|
|
||||||
binding = meta_models_v2.NetworkFlavor(flavor=flavor, network_id=net_id)
|
|
||||||
session.add(binding)
|
|
||||||
return binding
|
|
||||||
|
|
||||||
|
|
||||||
def get_flavor_by_router(session, router_id):
|
|
||||||
try:
|
|
||||||
binding = (session.query(meta_models_v2.RouterFlavor).
|
|
||||||
filter_by(router_id=router_id).
|
|
||||||
one())
|
|
||||||
except exc.NoResultFound:
|
|
||||||
return None
|
|
||||||
return binding.flavor
|
|
||||||
|
|
||||||
|
|
||||||
def add_router_flavor_binding(session, flavor, router_id):
|
|
||||||
binding = meta_models_v2.RouterFlavor(flavor=flavor, router_id=router_id)
|
|
||||||
session.add(binding)
|
|
||||||
return binding
|
|
@ -13,402 +13,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.
|
||||||
|
|
||||||
from oslo_config import cfg
|
from metaplugin.plugin import meta_neutron_plugin
|
||||||
from oslo_utils import importutils
|
|
||||||
|
|
||||||
from neutron.common import exceptions as exc
|
|
||||||
from neutron.common import topics
|
|
||||||
from neutron import context as neutron_context
|
|
||||||
from neutron.db import db_base_plugin_v2
|
|
||||||
from neutron.db import external_net_db
|
|
||||||
from neutron.db import extraroute_db
|
|
||||||
from neutron.db import l3_db
|
|
||||||
from neutron.db import models_v2
|
|
||||||
from neutron.extensions import flavor as ext_flavor
|
|
||||||
from neutron.i18n import _LE
|
|
||||||
from neutron.openstack.common import log as logging
|
|
||||||
from neutron.plugins.metaplugin.common import config # noqa
|
|
||||||
from neutron.plugins.metaplugin import meta_db_v2
|
|
||||||
from neutron.plugins.metaplugin import meta_models_v2
|
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
MetaPluginV2 = meta_neutron_plugin.MetaPluginV2
|
||||||
|
|
||||||
|
|
||||||
# Hooks used to select records which belong a target plugin.
|
|
||||||
def _meta_network_model_hook(context, original_model, query):
|
|
||||||
return query.outerjoin(meta_models_v2.NetworkFlavor,
|
|
||||||
meta_models_v2.NetworkFlavor.network_id ==
|
|
||||||
models_v2.Network.id)
|
|
||||||
|
|
||||||
|
|
||||||
def _meta_port_model_hook(context, original_model, query):
|
|
||||||
return query.join(meta_models_v2.NetworkFlavor,
|
|
||||||
meta_models_v2.NetworkFlavor.network_id ==
|
|
||||||
models_v2.Port.network_id)
|
|
||||||
|
|
||||||
|
|
||||||
def _meta_flavor_filter_hook(query, filters):
|
|
||||||
if ext_flavor.FLAVOR_NETWORK in filters:
|
|
||||||
return query.filter(meta_models_v2.NetworkFlavor.flavor ==
|
|
||||||
filters[ext_flavor.FLAVOR_NETWORK][0])
|
|
||||||
return query
|
|
||||||
|
|
||||||
|
|
||||||
# Metaplugin Exceptions
|
|
||||||
class FlavorNotFound(exc.NotFound):
|
|
||||||
message = _("Flavor %(flavor)s could not be found")
|
|
||||||
|
|
||||||
|
|
||||||
class FaildToAddFlavorBinding(exc.NeutronException):
|
|
||||||
message = _("Failed to add flavor binding")
|
|
||||||
|
|
||||||
|
|
||||||
class MetaPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
|
|
||||||
external_net_db.External_net_db_mixin,
|
|
||||||
extraroute_db.ExtraRoute_db_mixin):
|
|
||||||
|
|
||||||
def __init__(self, configfile=None):
|
|
||||||
super(MetaPluginV2, self).__init__()
|
|
||||||
LOG.debug("Start initializing metaplugin")
|
|
||||||
self.supported_extension_aliases = ['flavor', 'external-net']
|
|
||||||
if cfg.CONF.META.supported_extension_aliases:
|
|
||||||
cfg_aliases = cfg.CONF.META.supported_extension_aliases.split(',')
|
|
||||||
self.supported_extension_aliases += cfg_aliases
|
|
||||||
|
|
||||||
# Ignore config option overapping
|
|
||||||
def _is_opt_registered(opts, opt):
|
|
||||||
if opt.dest in opts:
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
cfg._is_opt_registered = _is_opt_registered
|
|
||||||
|
|
||||||
self.plugins = {}
|
|
||||||
|
|
||||||
plugin_list = [plugin_set.split(':')
|
|
||||||
for plugin_set
|
|
||||||
in cfg.CONF.META.plugin_list.split(',')]
|
|
||||||
self.rpc_flavor = cfg.CONF.META.rpc_flavor
|
|
||||||
topic_save = topics.PLUGIN
|
|
||||||
topic_fake = topic_save + '-metaplugin'
|
|
||||||
for flavor, plugin_provider in plugin_list:
|
|
||||||
# Rename topic used by a plugin other than rpc_flavor during
|
|
||||||
# loading the plugin instance if rpc_flavor is specified.
|
|
||||||
# This enforces the plugin specified by rpc_flavor is only
|
|
||||||
# consumer of 'q-plugin'. It is a bit tricky but there is no
|
|
||||||
# bad effect.
|
|
||||||
if self.rpc_flavor and self.rpc_flavor != flavor:
|
|
||||||
topics.PLUGIN = topic_fake
|
|
||||||
self.plugins[flavor] = self._load_plugin(plugin_provider)
|
|
||||||
topics.PLUGIN = topic_save
|
|
||||||
|
|
||||||
self.l3_plugins = {}
|
|
||||||
if cfg.CONF.META.l3_plugin_list:
|
|
||||||
l3_plugin_list = [plugin_set.split(':')
|
|
||||||
for plugin_set
|
|
||||||
in cfg.CONF.META.l3_plugin_list.split(',')]
|
|
||||||
for flavor, plugin_provider in l3_plugin_list:
|
|
||||||
if flavor in self.plugins:
|
|
||||||
self.l3_plugins[flavor] = self.plugins[flavor]
|
|
||||||
else:
|
|
||||||
# For l3 only plugin
|
|
||||||
self.l3_plugins[flavor] = self._load_plugin(
|
|
||||||
plugin_provider)
|
|
||||||
|
|
||||||
self.default_flavor = cfg.CONF.META.default_flavor
|
|
||||||
if self.default_flavor not in self.plugins:
|
|
||||||
raise exc.Invalid(_('default_flavor %s is not plugin list') %
|
|
||||||
self.default_flavor)
|
|
||||||
|
|
||||||
if self.l3_plugins:
|
|
||||||
self.default_l3_flavor = cfg.CONF.META.default_l3_flavor
|
|
||||||
if self.default_l3_flavor not in self.l3_plugins:
|
|
||||||
raise exc.Invalid(_('default_l3_flavor %s is not plugin list')
|
|
||||||
% self.default_l3_flavor)
|
|
||||||
self.supported_extension_aliases += ['router', 'ext-gw-mode',
|
|
||||||
'extraroute']
|
|
||||||
|
|
||||||
if self.rpc_flavor and self.rpc_flavor not in self.plugins:
|
|
||||||
raise exc.Invalid(_('rpc_flavor %s is not plugin list') %
|
|
||||||
self.rpc_flavor)
|
|
||||||
|
|
||||||
self.extension_map = {}
|
|
||||||
if not cfg.CONF.META.extension_map == '':
|
|
||||||
extension_list = [method_set.split(':')
|
|
||||||
for method_set
|
|
||||||
in cfg.CONF.META.extension_map.split(',')]
|
|
||||||
for method_name, flavor in extension_list:
|
|
||||||
self.extension_map[method_name] = flavor
|
|
||||||
|
|
||||||
# Register hooks.
|
|
||||||
# The hooks are applied for each target plugin instance when
|
|
||||||
# calling the base class to get networks/ports so that only records
|
|
||||||
# which belong to the plugin are selected.
|
|
||||||
#NOTE: Doing registration here (within __init__()) is to avoid
|
|
||||||
# registration when merely importing this file. This is only
|
|
||||||
# for running whole unit tests.
|
|
||||||
db_base_plugin_v2.NeutronDbPluginV2.register_model_query_hook(
|
|
||||||
models_v2.Network,
|
|
||||||
'metaplugin_net',
|
|
||||||
_meta_network_model_hook,
|
|
||||||
None,
|
|
||||||
_meta_flavor_filter_hook)
|
|
||||||
db_base_plugin_v2.NeutronDbPluginV2.register_model_query_hook(
|
|
||||||
models_v2.Port,
|
|
||||||
'metaplugin_port',
|
|
||||||
_meta_port_model_hook,
|
|
||||||
None,
|
|
||||||
_meta_flavor_filter_hook)
|
|
||||||
|
|
||||||
def _load_plugin(self, plugin_provider):
|
|
||||||
LOG.debug("Plugin location: %s", plugin_provider)
|
|
||||||
plugin_klass = importutils.import_class(plugin_provider)
|
|
||||||
return plugin_klass()
|
|
||||||
|
|
||||||
def _get_plugin(self, flavor):
|
|
||||||
if flavor not in self.plugins:
|
|
||||||
raise FlavorNotFound(flavor=flavor)
|
|
||||||
return self.plugins[flavor]
|
|
||||||
|
|
||||||
def _get_l3_plugin(self, flavor):
|
|
||||||
if flavor not in self.l3_plugins:
|
|
||||||
raise FlavorNotFound(flavor=flavor)
|
|
||||||
return self.l3_plugins[flavor]
|
|
||||||
|
|
||||||
def __getattr__(self, key):
|
|
||||||
# At first, try to pickup extension command from extension_map
|
|
||||||
|
|
||||||
if key in self.extension_map:
|
|
||||||
flavor = self.extension_map[key]
|
|
||||||
plugin = self._get_plugin(flavor)
|
|
||||||
if plugin and hasattr(plugin, key):
|
|
||||||
return getattr(plugin, key)
|
|
||||||
|
|
||||||
# Second, try to match extension method in order of plugin list
|
|
||||||
for flavor, plugin in self.plugins.items():
|
|
||||||
if hasattr(plugin, key):
|
|
||||||
return getattr(plugin, key)
|
|
||||||
|
|
||||||
# if no plugin support the method, then raise
|
|
||||||
raise AttributeError()
|
|
||||||
|
|
||||||
def _extend_network_dict(self, context, network):
|
|
||||||
flavor = self._get_flavor_by_network_id(context, network['id'])
|
|
||||||
network[ext_flavor.FLAVOR_NETWORK] = flavor
|
|
||||||
|
|
||||||
def start_rpc_listeners(self):
|
|
||||||
return self.plugins[self.rpc_flavor].start_rpc_listeners()
|
|
||||||
|
|
||||||
def rpc_workers_supported(self):
|
|
||||||
#NOTE: If a plugin which supports multiple RPC workers is desired
|
|
||||||
# to handle RPC, rpc_flavor must be specified.
|
|
||||||
return (self.rpc_flavor and
|
|
||||||
self.plugins[self.rpc_flavor].rpc_workers_supported())
|
|
||||||
|
|
||||||
def create_network(self, context, network):
|
|
||||||
n = network['network']
|
|
||||||
flavor = n.get(ext_flavor.FLAVOR_NETWORK)
|
|
||||||
if str(flavor) not in self.plugins:
|
|
||||||
flavor = self.default_flavor
|
|
||||||
plugin = self._get_plugin(flavor)
|
|
||||||
net = plugin.create_network(context, network)
|
|
||||||
LOG.debug("Created network: %(net_id)s with flavor "
|
|
||||||
"%(flavor)s", {'net_id': net['id'], 'flavor': flavor})
|
|
||||||
try:
|
|
||||||
meta_db_v2.add_network_flavor_binding(context.session,
|
|
||||||
flavor, str(net['id']))
|
|
||||||
except Exception:
|
|
||||||
LOG.exception(_LE('Failed to add flavor bindings'))
|
|
||||||
plugin.delete_network(context, net['id'])
|
|
||||||
raise FaildToAddFlavorBinding()
|
|
||||||
|
|
||||||
LOG.debug("Created network: %s", net['id'])
|
|
||||||
self._extend_network_dict(context, net)
|
|
||||||
return net
|
|
||||||
|
|
||||||
def update_network(self, context, id, network):
|
|
||||||
flavor = meta_db_v2.get_flavor_by_network(context.session, id)
|
|
||||||
plugin = self._get_plugin(flavor)
|
|
||||||
return plugin.update_network(context, id, network)
|
|
||||||
|
|
||||||
def delete_network(self, context, id):
|
|
||||||
flavor = meta_db_v2.get_flavor_by_network(context.session, id)
|
|
||||||
plugin = self._get_plugin(flavor)
|
|
||||||
return plugin.delete_network(context, id)
|
|
||||||
|
|
||||||
def get_network(self, context, id, fields=None):
|
|
||||||
flavor = meta_db_v2.get_flavor_by_network(context.session, id)
|
|
||||||
plugin = self._get_plugin(flavor)
|
|
||||||
net = plugin.get_network(context, id, fields)
|
|
||||||
net['id'] = id
|
|
||||||
if not fields or ext_flavor.FLAVOR_NETWORK in fields:
|
|
||||||
self._extend_network_dict(context, net)
|
|
||||||
if fields and 'id' not in fields:
|
|
||||||
del net['id']
|
|
||||||
return net
|
|
||||||
|
|
||||||
def get_networks(self, context, filters=None, fields=None):
|
|
||||||
nets = []
|
|
||||||
for flavor, plugin in self.plugins.items():
|
|
||||||
if (filters and ext_flavor.FLAVOR_NETWORK in filters and
|
|
||||||
flavor not in filters[ext_flavor.FLAVOR_NETWORK]):
|
|
||||||
continue
|
|
||||||
if filters:
|
|
||||||
#NOTE: copy each time since a target plugin may modify
|
|
||||||
# plugin_filters.
|
|
||||||
plugin_filters = filters.copy()
|
|
||||||
else:
|
|
||||||
plugin_filters = {}
|
|
||||||
plugin_filters[ext_flavor.FLAVOR_NETWORK] = [flavor]
|
|
||||||
plugin_nets = plugin.get_networks(context, plugin_filters, fields)
|
|
||||||
for net in plugin_nets:
|
|
||||||
if not fields or ext_flavor.FLAVOR_NETWORK in fields:
|
|
||||||
net[ext_flavor.FLAVOR_NETWORK] = flavor
|
|
||||||
nets.append(net)
|
|
||||||
return nets
|
|
||||||
|
|
||||||
def _get_flavor_by_network_id(self, context, network_id):
|
|
||||||
return meta_db_v2.get_flavor_by_network(context.session, network_id)
|
|
||||||
|
|
||||||
def _get_flavor_by_router_id(self, context, router_id):
|
|
||||||
return meta_db_v2.get_flavor_by_router(context.session, router_id)
|
|
||||||
|
|
||||||
def _get_plugin_by_network_id(self, context, network_id):
|
|
||||||
flavor = self._get_flavor_by_network_id(context, network_id)
|
|
||||||
return self._get_plugin(flavor)
|
|
||||||
|
|
||||||
def create_port(self, context, port):
|
|
||||||
p = port['port']
|
|
||||||
if 'network_id' not in p:
|
|
||||||
raise exc.NotFound()
|
|
||||||
plugin = self._get_plugin_by_network_id(context, p['network_id'])
|
|
||||||
return plugin.create_port(context, port)
|
|
||||||
|
|
||||||
def update_port(self, context, id, port):
|
|
||||||
port_in_db = self._get_port(context, id)
|
|
||||||
plugin = self._get_plugin_by_network_id(context,
|
|
||||||
port_in_db['network_id'])
|
|
||||||
return plugin.update_port(context, id, port)
|
|
||||||
|
|
||||||
def delete_port(self, context, id, l3_port_check=True):
|
|
||||||
port_in_db = self._get_port(context, id)
|
|
||||||
plugin = self._get_plugin_by_network_id(context,
|
|
||||||
port_in_db['network_id'])
|
|
||||||
return plugin.delete_port(context, id, l3_port_check)
|
|
||||||
|
|
||||||
# This is necessary since there is a case that
|
|
||||||
# NeutronManager.get_plugin()._make_port_dict is called.
|
|
||||||
def _make_port_dict(self, port):
|
|
||||||
context = neutron_context.get_admin_context()
|
|
||||||
plugin = self._get_plugin_by_network_id(context,
|
|
||||||
port['network_id'])
|
|
||||||
return plugin._make_port_dict(port)
|
|
||||||
|
|
||||||
def get_port(self, context, id, fields=None):
|
|
||||||
port_in_db = self._get_port(context, id)
|
|
||||||
plugin = self._get_plugin_by_network_id(context,
|
|
||||||
port_in_db['network_id'])
|
|
||||||
return plugin.get_port(context, id, fields)
|
|
||||||
|
|
||||||
def get_ports(self, context, filters=None, fields=None):
|
|
||||||
all_ports = []
|
|
||||||
for flavor, plugin in self.plugins.items():
|
|
||||||
if filters:
|
|
||||||
#NOTE: copy each time since a target plugin may modify
|
|
||||||
# plugin_filters.
|
|
||||||
plugin_filters = filters.copy()
|
|
||||||
else:
|
|
||||||
plugin_filters = {}
|
|
||||||
plugin_filters[ext_flavor.FLAVOR_NETWORK] = [flavor]
|
|
||||||
ports = plugin.get_ports(context, plugin_filters, fields)
|
|
||||||
all_ports += ports
|
|
||||||
return all_ports
|
|
||||||
|
|
||||||
def create_subnet(self, context, subnet):
|
|
||||||
s = subnet['subnet']
|
|
||||||
if 'network_id' not in s:
|
|
||||||
raise exc.NotFound()
|
|
||||||
plugin = self._get_plugin_by_network_id(context,
|
|
||||||
s['network_id'])
|
|
||||||
return plugin.create_subnet(context, subnet)
|
|
||||||
|
|
||||||
def update_subnet(self, context, id, subnet):
|
|
||||||
s = self.get_subnet(context, id)
|
|
||||||
plugin = self._get_plugin_by_network_id(context,
|
|
||||||
s['network_id'])
|
|
||||||
return plugin.update_subnet(context, id, subnet)
|
|
||||||
|
|
||||||
def delete_subnet(self, context, id):
|
|
||||||
s = self.get_subnet(context, id)
|
|
||||||
plugin = self._get_plugin_by_network_id(context,
|
|
||||||
s['network_id'])
|
|
||||||
return plugin.delete_subnet(context, id)
|
|
||||||
|
|
||||||
def _extend_router_dict(self, context, router):
|
|
||||||
flavor = self._get_flavor_by_router_id(context, router['id'])
|
|
||||||
router[ext_flavor.FLAVOR_ROUTER] = flavor
|
|
||||||
|
|
||||||
def create_router(self, context, router):
|
|
||||||
r = router['router']
|
|
||||||
flavor = r.get(ext_flavor.FLAVOR_ROUTER)
|
|
||||||
if str(flavor) not in self.l3_plugins:
|
|
||||||
flavor = self.default_l3_flavor
|
|
||||||
plugin = self._get_l3_plugin(flavor)
|
|
||||||
r_in_db = plugin.create_router(context, router)
|
|
||||||
LOG.debug("Created router: %(router_id)s with flavor "
|
|
||||||
"%(flavor)s",
|
|
||||||
{'router_id': r_in_db['id'], 'flavor': flavor})
|
|
||||||
try:
|
|
||||||
meta_db_v2.add_router_flavor_binding(context.session,
|
|
||||||
flavor, str(r_in_db['id']))
|
|
||||||
except Exception:
|
|
||||||
LOG.exception(_LE('Failed to add flavor bindings'))
|
|
||||||
plugin.delete_router(context, r_in_db['id'])
|
|
||||||
raise FaildToAddFlavorBinding()
|
|
||||||
|
|
||||||
LOG.debug("Created router: %s", r_in_db['id'])
|
|
||||||
self._extend_router_dict(context, r_in_db)
|
|
||||||
return r_in_db
|
|
||||||
|
|
||||||
def update_router(self, context, id, router):
|
|
||||||
flavor = meta_db_v2.get_flavor_by_router(context.session, id)
|
|
||||||
plugin = self._get_l3_plugin(flavor)
|
|
||||||
return plugin.update_router(context, id, router)
|
|
||||||
|
|
||||||
def delete_router(self, context, id):
|
|
||||||
flavor = meta_db_v2.get_flavor_by_router(context.session, id)
|
|
||||||
plugin = self._get_l3_plugin(flavor)
|
|
||||||
return plugin.delete_router(context, id)
|
|
||||||
|
|
||||||
def get_router(self, context, id, fields=None):
|
|
||||||
flavor = meta_db_v2.get_flavor_by_router(context.session, id)
|
|
||||||
plugin = self._get_l3_plugin(flavor)
|
|
||||||
router = plugin.get_router(context, id, fields)
|
|
||||||
if not fields or ext_flavor.FLAVOR_ROUTER in fields:
|
|
||||||
self._extend_router_dict(context, router)
|
|
||||||
return router
|
|
||||||
|
|
||||||
def get_routers_with_flavor(self, context, filters=None,
|
|
||||||
fields=None):
|
|
||||||
collection = self._model_query(context, l3_db.Router)
|
|
||||||
r_model = meta_models_v2.RouterFlavor
|
|
||||||
collection = collection.join(r_model,
|
|
||||||
l3_db.Router.id == r_model.router_id)
|
|
||||||
if filters:
|
|
||||||
for key, value in filters.iteritems():
|
|
||||||
if key == ext_flavor.FLAVOR_ROUTER:
|
|
||||||
column = meta_models_v2.RouterFlavor.flavor
|
|
||||||
else:
|
|
||||||
column = getattr(l3_db.Router, key, None)
|
|
||||||
if column:
|
|
||||||
collection = collection.filter(column.in_(value))
|
|
||||||
return [self._make_router_dict(c, fields) for c in collection]
|
|
||||||
|
|
||||||
def get_routers(self, context, filters=None, fields=None):
|
|
||||||
routers = self.get_routers_with_flavor(context, filters,
|
|
||||||
None)
|
|
||||||
return [self.get_router(context, router['id'],
|
|
||||||
fields)
|
|
||||||
for router in routers]
|
|
||||||
|
@ -13,123 +13,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.
|
||||||
|
|
||||||
from oslo_config import cfg
|
from metaplugin.plugin import proxy_neutron_plugin
|
||||||
|
|
||||||
from neutron.db import db_base_plugin_v2
|
|
||||||
from neutron.db import external_net_db
|
|
||||||
from neutron.db import l3_db
|
|
||||||
from neutron.i18n import _LE, _LW
|
|
||||||
from neutron.openstack.common import log as logging
|
|
||||||
from neutronclient.common import exceptions
|
|
||||||
from neutronclient.v2_0 import client
|
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
ProxyPluginV2 = proxy_neutron_plugin.ProxyPluginV2
|
||||||
|
|
||||||
|
|
||||||
class ProxyPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
|
|
||||||
external_net_db.External_net_db_mixin,
|
|
||||||
l3_db.L3_NAT_db_mixin):
|
|
||||||
supported_extension_aliases = ["external-net", "router"]
|
|
||||||
|
|
||||||
def __init__(self, configfile=None):
|
|
||||||
super(ProxyPluginV2, self).__init__()
|
|
||||||
self.neutron = client.Client(
|
|
||||||
username=cfg.CONF.PROXY.admin_user,
|
|
||||||
password=cfg.CONF.PROXY.admin_password,
|
|
||||||
tenant_name=cfg.CONF.PROXY.admin_tenant_name,
|
|
||||||
auth_url=cfg.CONF.PROXY.auth_url,
|
|
||||||
auth_strategy=cfg.CONF.PROXY.auth_strategy,
|
|
||||||
region_name=cfg.CONF.PROXY.auth_region
|
|
||||||
)
|
|
||||||
|
|
||||||
def _get_client(self):
|
|
||||||
return self.neutron
|
|
||||||
|
|
||||||
def create_subnet(self, context, subnet):
|
|
||||||
subnet_remote = self._get_client().create_subnet(subnet)
|
|
||||||
subnet['subnet']['id'] = subnet_remote['id']
|
|
||||||
tenant_id = self._get_tenant_id_for_create(context, subnet['subnet'])
|
|
||||||
subnet['subnet']['tenant_id'] = tenant_id
|
|
||||||
try:
|
|
||||||
subnet_in_db = super(ProxyPluginV2, self).create_subnet(
|
|
||||||
context, subnet)
|
|
||||||
except Exception:
|
|
||||||
self._get_client().delete_subnet(subnet_remote['id'])
|
|
||||||
return subnet_in_db
|
|
||||||
|
|
||||||
def update_subnet(self, context, id, subnet):
|
|
||||||
subnet_in_db = super(ProxyPluginV2, self).update_subnet(
|
|
||||||
context, id, subnet)
|
|
||||||
try:
|
|
||||||
self._get_client().update_subnet(id, subnet)
|
|
||||||
except Exception as e:
|
|
||||||
LOG.error(_LE("Update subnet failed: %s"), e)
|
|
||||||
return subnet_in_db
|
|
||||||
|
|
||||||
def delete_subnet(self, context, id):
|
|
||||||
try:
|
|
||||||
self._get_client().delete_subnet(id)
|
|
||||||
except exceptions.NotFound:
|
|
||||||
LOG.warn(_LW("Subnet in remote have already deleted"))
|
|
||||||
return super(ProxyPluginV2, self).delete_subnet(context, id)
|
|
||||||
|
|
||||||
def create_network(self, context, network):
|
|
||||||
network_remote = self._get_client().create_network(network)
|
|
||||||
network['network']['id'] = network_remote['id']
|
|
||||||
tenant_id = self._get_tenant_id_for_create(context, network['network'])
|
|
||||||
network['network']['tenant_id'] = tenant_id
|
|
||||||
try:
|
|
||||||
network_in_db = super(ProxyPluginV2, self).create_network(
|
|
||||||
context, network)
|
|
||||||
except Exception:
|
|
||||||
self._get_client().delete_network(network_remote['id'])
|
|
||||||
return network_in_db
|
|
||||||
|
|
||||||
def update_network(self, context, id, network):
|
|
||||||
network_in_db = super(ProxyPluginV2, self).update_network(
|
|
||||||
context, id, network)
|
|
||||||
try:
|
|
||||||
self._get_client().update_network(id, network)
|
|
||||||
except Exception as e:
|
|
||||||
LOG.error(_LE("Update network failed: %s"), e)
|
|
||||||
return network_in_db
|
|
||||||
|
|
||||||
def delete_network(self, context, id):
|
|
||||||
try:
|
|
||||||
self._get_client().delete_network(id)
|
|
||||||
except exceptions.NetworkNotFoundClient:
|
|
||||||
LOG.warn(_LW("Network in remote have already deleted"))
|
|
||||||
return super(ProxyPluginV2, self).delete_network(context, id)
|
|
||||||
|
|
||||||
def create_port(self, context, port):
|
|
||||||
port_remote = self._get_client().create_port(port)
|
|
||||||
port['port']['id'] = port_remote['id']
|
|
||||||
tenant_id = self._get_tenant_id_for_create(context, port['port'])
|
|
||||||
port['port']['tenant_id'] = tenant_id
|
|
||||||
try:
|
|
||||||
port_in_db = super(ProxyPluginV2, self).create_port(
|
|
||||||
context, port)
|
|
||||||
except Exception:
|
|
||||||
self._get_client().delete_port(port_remote['id'])
|
|
||||||
return port_in_db
|
|
||||||
|
|
||||||
def update_port(self, context, id, port):
|
|
||||||
port_in_db = super(ProxyPluginV2, self).update_port(
|
|
||||||
context, id, port)
|
|
||||||
try:
|
|
||||||
self._get_client().update_port(id, port)
|
|
||||||
except Exception as e:
|
|
||||||
LOG.error(_LE("Update port failed: %s"), e)
|
|
||||||
return port_in_db
|
|
||||||
|
|
||||||
def delete_port(self, context, id, l3_port_check=True):
|
|
||||||
if l3_port_check:
|
|
||||||
self.prevent_l3_port_deletion(context, id)
|
|
||||||
self.disassociate_floatingips(context, id)
|
|
||||||
|
|
||||||
try:
|
|
||||||
self._get_client().delete_port(id)
|
|
||||||
except exceptions.PortNotFoundClient:
|
|
||||||
LOG.warn(_LW("Port in remote have already deleted"))
|
|
||||||
return super(ProxyPluginV2, self).delete_port(context, id)
|
|
||||||
|
@ -1,77 +0,0 @@
|
|||||||
# Copyright 2012, Nachi Ueno, NTT MCL, Inc.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
from neutron.db import db_base_plugin_v2
|
|
||||||
from neutron.db import external_net_db
|
|
||||||
from neutron.db import l3_gwmode_db
|
|
||||||
|
|
||||||
|
|
||||||
class Fake1(db_base_plugin_v2.NeutronDbPluginV2,
|
|
||||||
external_net_db.External_net_db_mixin,
|
|
||||||
l3_gwmode_db.L3_NAT_db_mixin):
|
|
||||||
supported_extension_aliases = ['external-net', 'router']
|
|
||||||
|
|
||||||
def fake_func(self):
|
|
||||||
return 'fake1'
|
|
||||||
|
|
||||||
def create_network(self, context, network):
|
|
||||||
session = context.session
|
|
||||||
with session.begin(subtransactions=True):
|
|
||||||
net = super(Fake1, self).create_network(context, network)
|
|
||||||
self._process_l3_create(context, net, network['network'])
|
|
||||||
return net
|
|
||||||
|
|
||||||
def update_network(self, context, id, network):
|
|
||||||
session = context.session
|
|
||||||
with session.begin(subtransactions=True):
|
|
||||||
net = super(Fake1, self).update_network(context, id,
|
|
||||||
network)
|
|
||||||
self._process_l3_update(context, net, network['network'])
|
|
||||||
return net
|
|
||||||
|
|
||||||
def delete_network(self, context, id):
|
|
||||||
session = context.session
|
|
||||||
with session.begin(subtransactions=True):
|
|
||||||
self._process_l3_delete(context, id)
|
|
||||||
return super(Fake1, self).delete_network(context, id)
|
|
||||||
|
|
||||||
def create_port(self, context, port):
|
|
||||||
port = super(Fake1, self).create_port(context, port)
|
|
||||||
return port
|
|
||||||
|
|
||||||
def create_subnet(self, context, subnet):
|
|
||||||
subnet = super(Fake1, self).create_subnet(context, subnet)
|
|
||||||
return subnet
|
|
||||||
|
|
||||||
def update_port(self, context, id, port):
|
|
||||||
port = super(Fake1, self).update_port(context, id, port)
|
|
||||||
return port
|
|
||||||
|
|
||||||
def delete_port(self, context, id, l3_port_check=True):
|
|
||||||
if l3_port_check:
|
|
||||||
self.prevent_l3_port_deletion(context, id)
|
|
||||||
self.disassociate_floatingips(context, id)
|
|
||||||
return super(Fake1, self).delete_port(context, id)
|
|
||||||
|
|
||||||
|
|
||||||
class Fake2(Fake1):
|
|
||||||
def fake_func(self):
|
|
||||||
return 'fake2'
|
|
||||||
|
|
||||||
def fake_func2(self):
|
|
||||||
return 'fake2'
|
|
||||||
|
|
||||||
def start_rpc_listeners(self):
|
|
||||||
# return value is only used to confirm this method was called.
|
|
||||||
return 'OK'
|
|
@ -1,78 +0,0 @@
|
|||||||
# Copyright (c) 2012 OpenStack Foundation.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
# implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
|
|
||||||
from neutron.tests.unit.metaplugin import test_metaplugin
|
|
||||||
from neutron.tests.unit import test_db_plugin as test_plugin
|
|
||||||
from neutron.tests.unit import test_l3_plugin
|
|
||||||
|
|
||||||
|
|
||||||
class MetaPluginV2DBTestCase(test_plugin.NeutronDbPluginV2TestCase):
|
|
||||||
|
|
||||||
_plugin_name = ('neutron.plugins.metaplugin.'
|
|
||||||
'meta_neutron_plugin.MetaPluginV2')
|
|
||||||
|
|
||||||
def setUp(self, plugin=None, ext_mgr=None,
|
|
||||||
service_plugins=None):
|
|
||||||
# NOTE(salv-orlando): The plugin keyword argument is ignored,
|
|
||||||
# as this class will always invoke super with self._plugin_name.
|
|
||||||
# These keyword parameters ensure setUp methods always have the
|
|
||||||
# same signature.
|
|
||||||
test_metaplugin.setup_metaplugin_conf()
|
|
||||||
ext_mgr = ext_mgr or test_l3_plugin.L3TestExtensionManager()
|
|
||||||
self.addCleanup(test_metaplugin.unregister_meta_hooks)
|
|
||||||
super(MetaPluginV2DBTestCase, self).setUp(
|
|
||||||
plugin=self._plugin_name, ext_mgr=ext_mgr,
|
|
||||||
service_plugins=service_plugins)
|
|
||||||
|
|
||||||
|
|
||||||
class TestMetaBasicGet(test_plugin.TestBasicGet,
|
|
||||||
MetaPluginV2DBTestCase):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class TestMetaV2HTTPResponse(test_plugin.TestV2HTTPResponse,
|
|
||||||
MetaPluginV2DBTestCase):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class TestMetaPortsV2(test_plugin.TestPortsV2,
|
|
||||||
MetaPluginV2DBTestCase):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class TestMetaNetworksV2(test_plugin.TestNetworksV2,
|
|
||||||
MetaPluginV2DBTestCase):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class TestMetaSubnetsV2(test_plugin.TestSubnetsV2,
|
|
||||||
MetaPluginV2DBTestCase):
|
|
||||||
#TODO(nati) This test fails if we run all test, but It success just one
|
|
||||||
def test_update_subnet_route(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def test_update_subnet_dns_to_None(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def test_update_subnet_route_to_None(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def test_update_subnet_dns(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class TestMetaL3NatDBTestCase(test_l3_plugin.L3NatDBIntTestCase,
|
|
||||||
MetaPluginV2DBTestCase):
|
|
||||||
pass
|
|
@ -1,405 +0,0 @@
|
|||||||
# Copyright 2012, Nachi Ueno, NTT MCL, Inc.
|
|
||||||
# All Rights Reserved.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
import mock
|
|
||||||
from oslo_config import cfg
|
|
||||||
import testtools
|
|
||||||
|
|
||||||
from neutron.common import exceptions as exc
|
|
||||||
from neutron.common import topics
|
|
||||||
from neutron import context
|
|
||||||
from neutron.db import db_base_plugin_v2
|
|
||||||
from neutron.db import models_v2
|
|
||||||
from neutron.extensions import flavor as ext_flavor
|
|
||||||
from neutron.openstack.common import uuidutils
|
|
||||||
from neutron.plugins.metaplugin import meta_neutron_plugin
|
|
||||||
from neutron.tests.unit import testlib_api
|
|
||||||
from neutron.tests.unit import testlib_plugin
|
|
||||||
|
|
||||||
CONF_FILE = ""
|
|
||||||
META_PATH = "neutron.plugins.metaplugin"
|
|
||||||
FAKE_PATH = "neutron.tests.unit.metaplugin"
|
|
||||||
PROXY_PATH = "%s.proxy_neutron_plugin.ProxyPluginV2" % META_PATH
|
|
||||||
PLUGIN_LIST = """
|
|
||||||
fake1:%s.fake_plugin.Fake1,fake2:%s.fake_plugin.Fake2,proxy:%s
|
|
||||||
""".strip() % (FAKE_PATH, FAKE_PATH, PROXY_PATH)
|
|
||||||
L3_PLUGIN_LIST = """
|
|
||||||
fake1:%s.fake_plugin.Fake1,fake2:%s.fake_plugin.Fake2
|
|
||||||
""".strip() % (FAKE_PATH, FAKE_PATH)
|
|
||||||
|
|
||||||
|
|
||||||
def setup_metaplugin_conf(has_l3=True):
|
|
||||||
cfg.CONF.set_override('auth_url', 'http://localhost:35357/v2.0',
|
|
||||||
'PROXY')
|
|
||||||
cfg.CONF.set_override('auth_region', 'RegionOne', 'PROXY')
|
|
||||||
cfg.CONF.set_override('admin_user', 'neutron', 'PROXY')
|
|
||||||
cfg.CONF.set_override('admin_password', 'password', 'PROXY')
|
|
||||||
cfg.CONF.set_override('admin_tenant_name', 'service', 'PROXY')
|
|
||||||
cfg.CONF.set_override('plugin_list', PLUGIN_LIST, 'META')
|
|
||||||
if has_l3:
|
|
||||||
cfg.CONF.set_override('l3_plugin_list', L3_PLUGIN_LIST, 'META')
|
|
||||||
else:
|
|
||||||
cfg.CONF.set_override('l3_plugin_list', "", 'META')
|
|
||||||
cfg.CONF.set_override('default_flavor', 'fake2', 'META')
|
|
||||||
cfg.CONF.set_override('default_l3_flavor', 'fake1', 'META')
|
|
||||||
cfg.CONF.set_override('base_mac', "12:34:56:78:90:ab")
|
|
||||||
#TODO(nati) remove this after subnet quota change is merged
|
|
||||||
cfg.CONF.set_override('max_dns_nameservers', 10)
|
|
||||||
|
|
||||||
|
|
||||||
# Hooks registered by metaplugin must not exist for other plugins UT.
|
|
||||||
# So hooks must be unregistered (overwrite to None in fact).
|
|
||||||
def unregister_meta_hooks():
|
|
||||||
db_base_plugin_v2.NeutronDbPluginV2.register_model_query_hook(
|
|
||||||
models_v2.Network, 'metaplugin_net', None, None, None)
|
|
||||||
db_base_plugin_v2.NeutronDbPluginV2.register_model_query_hook(
|
|
||||||
models_v2.Port, 'metaplugin_port', None, None, None)
|
|
||||||
|
|
||||||
|
|
||||||
class MetaNeutronPluginV2Test(testlib_api.SqlTestCase,
|
|
||||||
testlib_plugin.PluginSetupHelper):
|
|
||||||
"""Class conisting of MetaNeutronPluginV2 unit tests."""
|
|
||||||
|
|
||||||
has_l3 = True
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
super(MetaNeutronPluginV2Test, self).setUp()
|
|
||||||
self.fake_tenant_id = uuidutils.generate_uuid()
|
|
||||||
self.context = context.get_admin_context()
|
|
||||||
|
|
||||||
self.addCleanup(unregister_meta_hooks)
|
|
||||||
|
|
||||||
setup_metaplugin_conf(self.has_l3)
|
|
||||||
|
|
||||||
self.client_cls_p = mock.patch('neutronclient.v2_0.client.Client')
|
|
||||||
client_cls = self.client_cls_p.start()
|
|
||||||
self.client_inst = mock.Mock()
|
|
||||||
client_cls.return_value = self.client_inst
|
|
||||||
self.client_inst.create_network.return_value = \
|
|
||||||
{'id': 'fake_id'}
|
|
||||||
self.client_inst.create_port.return_value = \
|
|
||||||
{'id': 'fake_id'}
|
|
||||||
self.client_inst.create_subnet.return_value = \
|
|
||||||
{'id': 'fake_id'}
|
|
||||||
self.client_inst.update_network.return_value = \
|
|
||||||
{'id': 'fake_id'}
|
|
||||||
self.client_inst.update_port.return_value = \
|
|
||||||
{'id': 'fake_id'}
|
|
||||||
self.client_inst.update_subnet.return_value = \
|
|
||||||
{'id': 'fake_id'}
|
|
||||||
self.client_inst.delete_network.return_value = True
|
|
||||||
self.client_inst.delete_port.return_value = True
|
|
||||||
self.client_inst.delete_subnet.return_value = True
|
|
||||||
plugin = (meta_neutron_plugin.MetaPluginV2.__module__ + '.'
|
|
||||||
+ meta_neutron_plugin.MetaPluginV2.__name__)
|
|
||||||
self.setup_coreplugin(plugin)
|
|
||||||
self.plugin = meta_neutron_plugin.MetaPluginV2(configfile=None)
|
|
||||||
|
|
||||||
def _fake_network(self, flavor):
|
|
||||||
data = {'network': {'name': flavor,
|
|
||||||
'admin_state_up': True,
|
|
||||||
'shared': False,
|
|
||||||
'router:external': [],
|
|
||||||
'tenant_id': self.fake_tenant_id,
|
|
||||||
ext_flavor.FLAVOR_NETWORK: flavor}}
|
|
||||||
return data
|
|
||||||
|
|
||||||
def _fake_port(self, net_id):
|
|
||||||
return {'port': {'name': net_id,
|
|
||||||
'network_id': net_id,
|
|
||||||
'admin_state_up': True,
|
|
||||||
'device_id': 'bad_device_id',
|
|
||||||
'device_owner': 'bad_device_owner',
|
|
||||||
'admin_state_up': True,
|
|
||||||
'host_routes': [],
|
|
||||||
'fixed_ips': [],
|
|
||||||
'mac_address': self.plugin._generate_mac(),
|
|
||||||
'tenant_id': self.fake_tenant_id}}
|
|
||||||
|
|
||||||
def _fake_subnet(self, net_id):
|
|
||||||
allocation_pools = [{'start': '10.0.0.2',
|
|
||||||
'end': '10.0.0.254'}]
|
|
||||||
return {'subnet': {'name': net_id,
|
|
||||||
'network_id': net_id,
|
|
||||||
'gateway_ip': '10.0.0.1',
|
|
||||||
'dns_nameservers': ['10.0.0.2'],
|
|
||||||
'host_routes': [],
|
|
||||||
'cidr': '10.0.0.0/24',
|
|
||||||
'allocation_pools': allocation_pools,
|
|
||||||
'enable_dhcp': True,
|
|
||||||
'ip_version': 4}}
|
|
||||||
|
|
||||||
def _fake_router(self, flavor):
|
|
||||||
data = {'router': {'name': flavor, 'admin_state_up': True,
|
|
||||||
'tenant_id': self.fake_tenant_id,
|
|
||||||
ext_flavor.FLAVOR_ROUTER: flavor,
|
|
||||||
'external_gateway_info': None}}
|
|
||||||
return data
|
|
||||||
|
|
||||||
def test_create_delete_network(self):
|
|
||||||
network1 = self._fake_network('fake1')
|
|
||||||
ret1 = self.plugin.create_network(self.context, network1)
|
|
||||||
self.assertEqual('fake1', ret1[ext_flavor.FLAVOR_NETWORK])
|
|
||||||
|
|
||||||
network2 = self._fake_network('fake2')
|
|
||||||
ret2 = self.plugin.create_network(self.context, network2)
|
|
||||||
self.assertEqual('fake2', ret2[ext_flavor.FLAVOR_NETWORK])
|
|
||||||
|
|
||||||
network3 = self._fake_network('proxy')
|
|
||||||
ret3 = self.plugin.create_network(self.context, network3)
|
|
||||||
self.assertEqual('proxy', ret3[ext_flavor.FLAVOR_NETWORK])
|
|
||||||
|
|
||||||
db_ret1 = self.plugin.get_network(self.context, ret1['id'])
|
|
||||||
self.assertEqual('fake1', db_ret1['name'])
|
|
||||||
|
|
||||||
db_ret2 = self.plugin.get_network(self.context, ret2['id'])
|
|
||||||
self.assertEqual('fake2', db_ret2['name'])
|
|
||||||
|
|
||||||
db_ret3 = self.plugin.get_network(self.context, ret3['id'])
|
|
||||||
self.assertEqual('proxy', db_ret3['name'])
|
|
||||||
|
|
||||||
db_ret4 = self.plugin.get_networks(self.context)
|
|
||||||
self.assertEqual(3, len(db_ret4))
|
|
||||||
|
|
||||||
db_ret5 = self.plugin.get_networks(
|
|
||||||
self.context,
|
|
||||||
{ext_flavor.FLAVOR_NETWORK: ['fake1']})
|
|
||||||
self.assertEqual(1, len(db_ret5))
|
|
||||||
self.assertEqual('fake1', db_ret5[0]['name'])
|
|
||||||
self.plugin.delete_network(self.context, ret1['id'])
|
|
||||||
self.plugin.delete_network(self.context, ret2['id'])
|
|
||||||
self.plugin.delete_network(self.context, ret3['id'])
|
|
||||||
|
|
||||||
def test_create_delete_port(self):
|
|
||||||
network1 = self._fake_network('fake1')
|
|
||||||
network_ret1 = self.plugin.create_network(self.context, network1)
|
|
||||||
network2 = self._fake_network('fake2')
|
|
||||||
network_ret2 = self.plugin.create_network(self.context, network2)
|
|
||||||
network3 = self._fake_network('proxy')
|
|
||||||
network_ret3 = self.plugin.create_network(self.context, network3)
|
|
||||||
|
|
||||||
port1 = self._fake_port(network_ret1['id'])
|
|
||||||
port2 = self._fake_port(network_ret2['id'])
|
|
||||||
port3 = self._fake_port(network_ret3['id'])
|
|
||||||
|
|
||||||
port1_ret = self.plugin.create_port(self.context, port1)
|
|
||||||
port2_ret = self.plugin.create_port(self.context, port2)
|
|
||||||
port3_ret = self.plugin.create_port(self.context, port3)
|
|
||||||
ports_all = self.plugin.get_ports(self.context)
|
|
||||||
|
|
||||||
self.assertEqual(network_ret1['id'], port1_ret['network_id'])
|
|
||||||
self.assertEqual(network_ret2['id'], port2_ret['network_id'])
|
|
||||||
self.assertEqual(network_ret3['id'], port3_ret['network_id'])
|
|
||||||
self.assertEqual(3, len(ports_all))
|
|
||||||
|
|
||||||
port1_dict = self.plugin._make_port_dict(port1_ret)
|
|
||||||
port2_dict = self.plugin._make_port_dict(port2_ret)
|
|
||||||
port3_dict = self.plugin._make_port_dict(port3_ret)
|
|
||||||
|
|
||||||
self.assertEqual(port1_dict, port1_ret)
|
|
||||||
self.assertEqual(port2_dict, port2_ret)
|
|
||||||
self.assertEqual(port3_dict, port3_ret)
|
|
||||||
|
|
||||||
port1['port']['admin_state_up'] = False
|
|
||||||
port2['port']['admin_state_up'] = False
|
|
||||||
port3['port']['admin_state_up'] = False
|
|
||||||
self.plugin.update_port(self.context, port1_ret['id'], port1)
|
|
||||||
self.plugin.update_port(self.context, port2_ret['id'], port2)
|
|
||||||
self.plugin.update_port(self.context, port3_ret['id'], port3)
|
|
||||||
port_in_db1 = self.plugin.get_port(self.context, port1_ret['id'])
|
|
||||||
port_in_db2 = self.plugin.get_port(self.context, port2_ret['id'])
|
|
||||||
port_in_db3 = self.plugin.get_port(self.context, port3_ret['id'])
|
|
||||||
self.assertEqual(False, port_in_db1['admin_state_up'])
|
|
||||||
self.assertEqual(False, port_in_db2['admin_state_up'])
|
|
||||||
self.assertEqual(False, port_in_db3['admin_state_up'])
|
|
||||||
|
|
||||||
self.plugin.delete_port(self.context, port1_ret['id'])
|
|
||||||
self.plugin.delete_port(self.context, port2_ret['id'])
|
|
||||||
self.plugin.delete_port(self.context, port3_ret['id'])
|
|
||||||
|
|
||||||
self.plugin.delete_network(self.context, network_ret1['id'])
|
|
||||||
self.plugin.delete_network(self.context, network_ret2['id'])
|
|
||||||
self.plugin.delete_network(self.context, network_ret3['id'])
|
|
||||||
|
|
||||||
def test_create_delete_subnet(self):
|
|
||||||
# for this test we need to enable overlapping ips
|
|
||||||
cfg.CONF.set_default('allow_overlapping_ips', True)
|
|
||||||
network1 = self._fake_network('fake1')
|
|
||||||
network_ret1 = self.plugin.create_network(self.context, network1)
|
|
||||||
network2 = self._fake_network('fake2')
|
|
||||||
network_ret2 = self.plugin.create_network(self.context, network2)
|
|
||||||
network3 = self._fake_network('proxy')
|
|
||||||
network_ret3 = self.plugin.create_network(self.context, network3)
|
|
||||||
|
|
||||||
subnet1 = self._fake_subnet(network_ret1['id'])
|
|
||||||
subnet2 = self._fake_subnet(network_ret2['id'])
|
|
||||||
subnet3 = self._fake_subnet(network_ret3['id'])
|
|
||||||
|
|
||||||
subnet1_ret = self.plugin.create_subnet(self.context, subnet1)
|
|
||||||
subnet2_ret = self.plugin.create_subnet(self.context, subnet2)
|
|
||||||
subnet3_ret = self.plugin.create_subnet(self.context, subnet3)
|
|
||||||
self.assertEqual(network_ret1['id'], subnet1_ret['network_id'])
|
|
||||||
self.assertEqual(network_ret2['id'], subnet2_ret['network_id'])
|
|
||||||
self.assertEqual(network_ret3['id'], subnet3_ret['network_id'])
|
|
||||||
|
|
||||||
subnet_in_db1 = self.plugin.get_subnet(self.context, subnet1_ret['id'])
|
|
||||||
subnet_in_db2 = self.plugin.get_subnet(self.context, subnet2_ret['id'])
|
|
||||||
subnet_in_db3 = self.plugin.get_subnet(self.context, subnet3_ret['id'])
|
|
||||||
|
|
||||||
subnet1['subnet']['allocation_pools'].pop()
|
|
||||||
subnet2['subnet']['allocation_pools'].pop()
|
|
||||||
subnet3['subnet']['allocation_pools'].pop()
|
|
||||||
|
|
||||||
self.plugin.update_subnet(self.context,
|
|
||||||
subnet1_ret['id'], subnet1)
|
|
||||||
self.plugin.update_subnet(self.context,
|
|
||||||
subnet2_ret['id'], subnet2)
|
|
||||||
self.plugin.update_subnet(self.context,
|
|
||||||
subnet3_ret['id'], subnet3)
|
|
||||||
subnet_in_db1 = self.plugin.get_subnet(self.context, subnet1_ret['id'])
|
|
||||||
subnet_in_db2 = self.plugin.get_subnet(self.context, subnet2_ret['id'])
|
|
||||||
subnet_in_db3 = self.plugin.get_subnet(self.context, subnet3_ret['id'])
|
|
||||||
|
|
||||||
self.assertEqual(4, subnet_in_db1['ip_version'])
|
|
||||||
self.assertEqual(4, subnet_in_db2['ip_version'])
|
|
||||||
self.assertEqual(4, subnet_in_db3['ip_version'])
|
|
||||||
|
|
||||||
self.plugin.delete_subnet(self.context, subnet1_ret['id'])
|
|
||||||
self.plugin.delete_subnet(self.context, subnet2_ret['id'])
|
|
||||||
self.plugin.delete_subnet(self.context, subnet3_ret['id'])
|
|
||||||
|
|
||||||
self.plugin.delete_network(self.context, network_ret1['id'])
|
|
||||||
self.plugin.delete_network(self.context, network_ret2['id'])
|
|
||||||
self.plugin.delete_network(self.context, network_ret3['id'])
|
|
||||||
|
|
||||||
def test_create_delete_router(self):
|
|
||||||
router1 = self._fake_router('fake1')
|
|
||||||
router_ret1 = self.plugin.create_router(self.context, router1)
|
|
||||||
router2 = self._fake_router('fake2')
|
|
||||||
router_ret2 = self.plugin.create_router(self.context, router2)
|
|
||||||
|
|
||||||
self.assertEqual('fake1', router_ret1[ext_flavor.FLAVOR_ROUTER])
|
|
||||||
self.assertEqual('fake2', router_ret2[ext_flavor.FLAVOR_ROUTER])
|
|
||||||
|
|
||||||
router_in_db1 = self.plugin.get_router(self.context, router_ret1['id'])
|
|
||||||
router_in_db2 = self.plugin.get_router(self.context, router_ret2['id'])
|
|
||||||
|
|
||||||
self.assertEqual('fake1', router_in_db1[ext_flavor.FLAVOR_ROUTER])
|
|
||||||
self.assertEqual('fake2', router_in_db2[ext_flavor.FLAVOR_ROUTER])
|
|
||||||
|
|
||||||
self.plugin.delete_router(self.context, router_ret1['id'])
|
|
||||||
self.plugin.delete_router(self.context, router_ret2['id'])
|
|
||||||
with testtools.ExpectedException(meta_neutron_plugin.FlavorNotFound):
|
|
||||||
self.plugin.get_router(self.context, router_ret1['id'])
|
|
||||||
|
|
||||||
def test_extension_method(self):
|
|
||||||
"""Test if plugin methods are accessible from self.plugin
|
|
||||||
|
|
||||||
This test compensates for the nondeterministic ordering of
|
|
||||||
self.plugin's plugins dictionary. Fake Plugin 1 and Fake Plugin 2
|
|
||||||
both have a function called fake_func and the order of
|
|
||||||
self.plugin.plugins will determine which fake_func is called.
|
|
||||||
"""
|
|
||||||
fake1 = self.plugin.plugins.keys().index('fake1')
|
|
||||||
fake2 = self.plugin.plugins.keys().index('fake2')
|
|
||||||
fake1_before_fake2 = fake1 < fake2
|
|
||||||
|
|
||||||
fake_func_return = 'fake1' if fake1_before_fake2 else 'fake2'
|
|
||||||
|
|
||||||
self.assertEqual(fake_func_return, self.plugin.fake_func())
|
|
||||||
self.assertEqual('fake2', self.plugin.fake_func2())
|
|
||||||
|
|
||||||
def test_extension_not_implemented_method(self):
|
|
||||||
try:
|
|
||||||
self.plugin.not_implemented()
|
|
||||||
except AttributeError:
|
|
||||||
return
|
|
||||||
except Exception:
|
|
||||||
self.fail("AttributeError Error is not raised")
|
|
||||||
|
|
||||||
self.fail("No Error is not raised")
|
|
||||||
|
|
||||||
def test_create_network_flavor_fail(self):
|
|
||||||
with mock.patch('neutron.plugins.metaplugin.meta_db_v2.'
|
|
||||||
'add_network_flavor_binding',
|
|
||||||
side_effect=Exception):
|
|
||||||
network = self._fake_network('fake1')
|
|
||||||
self.assertRaises(meta_neutron_plugin.FaildToAddFlavorBinding,
|
|
||||||
self.plugin.create_network,
|
|
||||||
self.context,
|
|
||||||
network)
|
|
||||||
count = self.plugin.get_networks_count(self.context)
|
|
||||||
self.assertEqual(count, 0)
|
|
||||||
|
|
||||||
def test_create_router_flavor_fail(self):
|
|
||||||
with mock.patch('neutron.plugins.metaplugin.meta_db_v2.'
|
|
||||||
'add_router_flavor_binding',
|
|
||||||
side_effect=Exception):
|
|
||||||
router = self._fake_router('fake1')
|
|
||||||
self.assertRaises(meta_neutron_plugin.FaildToAddFlavorBinding,
|
|
||||||
self.plugin.create_router,
|
|
||||||
self.context,
|
|
||||||
router)
|
|
||||||
count = self.plugin.get_routers_count(self.context)
|
|
||||||
self.assertEqual(count, 0)
|
|
||||||
|
|
||||||
|
|
||||||
class MetaNeutronPluginV2TestWithoutL3(MetaNeutronPluginV2Test):
|
|
||||||
"""Tests without l3_plugin_list configration."""
|
|
||||||
|
|
||||||
has_l3 = False
|
|
||||||
|
|
||||||
def test_supported_extension_aliases(self):
|
|
||||||
self.assertEqual(self.plugin.supported_extension_aliases,
|
|
||||||
['flavor', 'external-net'])
|
|
||||||
|
|
||||||
def test_create_delete_router(self):
|
|
||||||
self.skipTest("Test case without router")
|
|
||||||
|
|
||||||
def test_create_router_flavor_fail(self):
|
|
||||||
self.skipTest("Test case without router")
|
|
||||||
|
|
||||||
|
|
||||||
class MetaNeutronPluginV2TestRpcFlavor(testlib_api.SqlTestCase):
|
|
||||||
"""Tests for rpc_flavor."""
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
super(MetaNeutronPluginV2TestRpcFlavor, self).setUp()
|
|
||||||
self.addCleanup(unregister_meta_hooks)
|
|
||||||
|
|
||||||
def test_rpc_flavor(self):
|
|
||||||
setup_metaplugin_conf()
|
|
||||||
cfg.CONF.set_override('rpc_flavor', 'fake1', 'META')
|
|
||||||
self.plugin = meta_neutron_plugin.MetaPluginV2()
|
|
||||||
self.assertEqual(topics.PLUGIN, 'q-plugin')
|
|
||||||
ret = self.plugin.rpc_workers_supported()
|
|
||||||
self.assertFalse(ret)
|
|
||||||
|
|
||||||
def test_invalid_rpc_flavor(self):
|
|
||||||
setup_metaplugin_conf()
|
|
||||||
cfg.CONF.set_override('rpc_flavor', 'fake-fake', 'META')
|
|
||||||
self.assertRaises(exc.Invalid,
|
|
||||||
meta_neutron_plugin.MetaPluginV2)
|
|
||||||
self.assertEqual(topics.PLUGIN, 'q-plugin')
|
|
||||||
|
|
||||||
def test_rpc_flavor_multiple_rpc_workers(self):
|
|
||||||
setup_metaplugin_conf()
|
|
||||||
cfg.CONF.set_override('rpc_flavor', 'fake2', 'META')
|
|
||||||
self.plugin = meta_neutron_plugin.MetaPluginV2()
|
|
||||||
self.assertEqual(topics.PLUGIN, 'q-plugin')
|
|
||||||
ret = self.plugin.rpc_workers_supported()
|
|
||||||
self.assertTrue(ret)
|
|
||||||
ret = self.plugin.start_rpc_listeners()
|
|
||||||
self.assertEqual('OK', ret)
|
|
Loading…
Reference in New Issue
Block a user