3a75160742
Added check to neutron.api.extensions.PluginAwareExtensionManager which raises an exception when an alias in the plugin's `supported_extension_aliases` list is not found in the set of loaded extension aliases. If an alias is missing, it means the extension for that alias has not been loaded, has not been found, and the file is missing from paths listed in `oslo.config.CONF.api_extensions_path`. This guards against a common class of bugs in plugins, such as typographical errors in the `supported_extension_aliases` property. Plugin changes: * bigswitch.plugin: Moves api_extensions_path override to plugin's __init__ method, similar to other plugins. * cisco.n1kv.n1kv_neutron_plugin: Removes "policy_profile_binding" and "network_profile_binding" as they don't exist in Neutron currently. Removed override of api_extensions_path as it is loaded through cisco.network_plugin. * cisco.network_plugin: Renames "Cisco Credential" to "credential". Adds api_extension_path override to plugin's __init__ method. * metaplugin.meta_neutron_plugin: Avoids alias of empty string when cfg.CONF.META.supported_extension_aliases is an empty string. * midonet.plugin: Fixes regression of98e16a06
from715b16ac
. * nec.nec_plugin: Extended override of api_extensions_path to append nec extensions path to existing configured path. * nicira.NeutronPlugin: Extended override of api_extensions_path to append NXP_EXT_PATH to existing configured path. Fixes: bug 1225080 Change-Id: Idcaade221d83c611fcbd87b503b2c8377d106962
342 lines
14 KiB
Python
342 lines
14 KiB
Python
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
|
|
# 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 oslo.config import cfg
|
|
|
|
from neutron.common import exceptions as exc
|
|
from neutron.db import api as db
|
|
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.flavor import (FLAVOR_NETWORK, FLAVOR_ROUTER)
|
|
from neutron.openstack.common import importutils
|
|
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.meta_models_v2 import (NetworkFlavor,
|
|
RouterFlavor)
|
|
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
# 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):
|
|
LOG.debug(_("Start initializing metaplugin"))
|
|
self.supported_extension_aliases = ['flavor', 'external-net',
|
|
'router', 'ext-gw-mode',
|
|
'extraroute']
|
|
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
|
|
|
|
# Keep existing tables if multiple plugin use same table name.
|
|
db.model_base.NeutronBase.__table_args__ = {'keep_existing': True}
|
|
|
|
self.plugins = {}
|
|
|
|
plugin_list = [plugin_set.split(':')
|
|
for plugin_set
|
|
in cfg.CONF.META.plugin_list.split(',')]
|
|
for flavor, plugin_provider in plugin_list:
|
|
self.plugins[flavor] = self._load_plugin(plugin_provider)
|
|
|
|
self.l3_plugins = {}
|
|
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)
|
|
|
|
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)
|
|
|
|
db.configure_db()
|
|
|
|
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
|
|
|
|
self.default_flavor = cfg.CONF.META.default_flavor
|
|
|
|
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[FLAVOR_NETWORK] = flavor
|
|
|
|
def _is_l3_plugin(self, plugin):
|
|
if hasattr(plugin, 'supported_extension_aliases'):
|
|
return 'router' in plugin.supported_extension_aliases
|
|
return False
|
|
|
|
def create_network(self, context, network):
|
|
n = network['network']
|
|
flavor = n.get(FLAVOR_NETWORK)
|
|
if str(flavor) not in self.plugins:
|
|
flavor = self.default_flavor
|
|
plugin = self._get_plugin(flavor)
|
|
with context.session.begin(subtransactions=True):
|
|
net = plugin.create_network(context, network)
|
|
if not self._is_l3_plugin(plugin):
|
|
self._process_l3_create(context, net, network['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(_('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)
|
|
with context.session.begin(subtransactions=True):
|
|
net = plugin.update_network(context, id, network)
|
|
if not self._is_l3_plugin(plugin):
|
|
self._process_l3_update(context, net, network['network'])
|
|
return net
|
|
|
|
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 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_with_flavor(self, context, filters=None,
|
|
fields=None):
|
|
collection = self._model_query(context, models_v2.Network)
|
|
model = NetworkFlavor
|
|
collection = collection.join(model,
|
|
models_v2.Network.id == model.network_id)
|
|
if filters:
|
|
for key, value in filters.iteritems():
|
|
if key == FLAVOR_NETWORK:
|
|
column = NetworkFlavor.flavor
|
|
else:
|
|
column = getattr(models_v2.Network, key, None)
|
|
if column:
|
|
collection = collection.filter(column.in_(value))
|
|
return [self._make_network_dict(c, fields) for c in collection]
|
|
|
|
def get_networks(self, context, filters=None, fields=None):
|
|
nets = self.get_networks_with_flavor(context, filters, None)
|
|
if filters:
|
|
nets = self._filter_nets_l3(context, nets, filters)
|
|
nets = [self.get_network(context, net['id'], fields)
|
|
for net in nets]
|
|
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'])
|
|
if l3_port_check:
|
|
self.prevent_l3_port_deletion(context, id)
|
|
self.disassociate_floatingips(context, id)
|
|
return plugin.delete_port(context, id, l3_port_check)
|
|
|
|
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[FLAVOR_ROUTER] = flavor
|
|
|
|
def create_router(self, context, router):
|
|
r = router['router']
|
|
flavor = r.get(FLAVOR_ROUTER)
|
|
if str(flavor) not in self.l3_plugins:
|
|
flavor = self.default_l3_flavor
|
|
plugin = self._get_l3_plugin(flavor)
|
|
with context.session.begin(subtransactions=True):
|
|
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})
|
|
meta_db_v2.add_router_flavor_binding(context.session,
|
|
flavor, str(r_in_db['id']))
|
|
|
|
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 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 = RouterFlavor
|
|
collection = collection.join(r_model,
|
|
l3_db.Router.id == r_model.router_id)
|
|
if filters:
|
|
for key, value in filters.iteritems():
|
|
if key == FLAVOR_ROUTER:
|
|
column = 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]
|