Now that we explicitly start multiple RPC servers, renamed the method to reflect behaviour. blueprint oslo-messaging Change-Id: I7c51cc1b44b4b4c8899255b9bedce1792c38634e
		
			
				
	
	
		
			420 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			420 lines
		
	
	
		
			17 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.common import topics
 | 
						|
from neutron import context as neutron_context
 | 
						|
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 import flavor as ext_flavor
 | 
						|
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 import meta_models_v2
 | 
						|
 | 
						|
 | 
						|
LOG = logging.getLogger(__name__)
 | 
						|
 | 
						|
 | 
						|
# 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
 | 
						|
 | 
						|
        # 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(',')]
 | 
						|
        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(_('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
 | 
						|
                    not flavor 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(_('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]
 |