vmware-nsx/quantum/plugins/metaplugin/meta_quantum_plugin.py
Nachi Ueno 7811e2a7c3 Initial implemention of MetaPlugin
This plugin supports multiple plugins at same time. This plugin is for L3 connectivility
between networks which are realized by different plugins. This plugin add new attribute 'flavor:id'.
flavor:id correspond to specific plugin. flavor-plugin mapping could be configureable by plugin_list config.
This plugin also support extensions. We can map extension to plugin by using extension_map config.

Implements blueprint metaplugin

Change-Id: Ia94d2349fb3ce9f121bbd2505324ae6f0c34247a
2012-08-13 06:19:31 +00:00

217 lines
8.6 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.
import logging
from quantum.common import exceptions as exc
from quantum.api.v2 import attributes
from quantum.common.utils import find_config_file
from quantum.db import api as db
from quantum.db import db_base_plugin_v2
from quantum.db import models_v2
from quantum.openstack.common import cfg
from quantum.openstack.common import importutils
from quantum.plugins.metaplugin.common import config
from quantum.plugins.metaplugin import meta_db_v2
from quantum.plugins.metaplugin.meta_models_v2 import Flavor
from quantum import policy
LOG = logging.getLogger("metaplugin")
class MetaPluginV2(db_base_plugin_v2.QuantumDbPluginV2):
def __init__(self, configfile=None):
LOG.debug("Start initializing metaplugin")
options = {"sql_connection": cfg.CONF.DATABASE.sql_connection}
options.update({'base': models_v2.model_base.BASEV2})
sql_max_retries = cfg.CONF.DATABASE.sql_max_retries
options.update({"sql_max_retries": sql_max_retries})
reconnect_interval = cfg.CONF.DATABASE.reconnect_interval
options.update({"reconnect_interval": reconnect_interval})
self.supported_extension_aliases = \
cfg.CONF.META.supported_extension_aliases.split(',')
self.supported_extension_aliases.append('flavor')
# 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.QuantumBase.__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.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
if not self.default_flavor in self.plugins:
raise exc.Invalid('default_flavor %s is not plugin list' %
self.default_flavor)
def _load_plugin(self, plugin_provider):
LOG.debug("Plugin location:%s", plugin_provider)
# If the plugin can't be found let them know gracefully
try:
LOG.info("Loading Plugin: %s" % plugin_provider)
plugin_klass = importutils.import_class(plugin_provider)
except exc.ClassNotFound:
LOG.exception("Error loading plugin")
raise Exception("Plugin not found. You can install a "
"plugin with: pip install <plugin-name>\n"
"Example: pip install quantum-sample-plugin")
return plugin_klass()
def _get_plugin(self, flavor):
if not flavor in self.plugins:
raise Exception("Plugin for flavor %s not found." % flavor)
return self.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 pluign 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):
network['flavor:id'] = self._get_flavor_by_network_id(network['id'])
def create_network(self, context, network):
n = network['network']
flavor = n.get('flavor:id')
if not str(flavor) in self.plugins:
flavor = self.default_flavor
plugin = self._get_plugin(flavor)
net = plugin.create_network(context, network)
LOG.debug("Created network: %s with flavor %s " % (net['id'], flavor))
try:
meta_db_v2.add_flavor_binding(flavor, str(net['id']))
except Exception as e:
LOG.error('failed to add flavor bindings')
plugin.delete_network(context, net['id'])
raise Exception('Failed to create network')
LOG.debug("Created network: %s" % net['id'])
self._extend_network_dict(context, net)
return net
def delete_network(self, context, id):
flavor = meta_db_v2.get_flavor_by_network(id)
plugin = self._get_plugin(flavor)
return plugin.delete_network(context, id)
def get_network(self, context, id, fields=None, verbose=None):
flavor = meta_db_v2.get_flavor_by_network(id)
plugin = self._get_plugin(flavor)
net = plugin.get_network(context, id, fields, verbose)
if not fields or 'flavor:id' in fields:
self._extend_network_dict(context, net)
return net
def get_networks_with_flavor(self, context, filters=None,
fields=None, verbose=None):
collection = self._model_query(context, models_v2.Network)
collection = collection.join(Flavor,
models_v2.Network.id == Flavor.network_id)
if filters:
for key, value in filters.iteritems():
if key == 'flavor:id':
column = Flavor.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.all()]
def get_networks(self, context, filters=None, fields=None, verbose=None):
nets = self.get_networks_with_flavor(context, filters,
None, verbose)
return [self.get_network(context, net['id'],
fields, verbose)
for net in nets]
def _get_flavor_by_network_id(self, network_id):
return meta_db_v2.get_flavor_by_network(network_id)
def _get_plugin_by_network_id(self, network_id):
flavor = self._get_flavor_by_network_id(network_id)
return self._get_plugin(flavor)
def create_port(self, context, port):
p = port['port']
if not 'network_id' in p:
raise exc.NotFound
plugin = self._get_plugin_by_network_id(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(port_in_db['network_id'])
return plugin.update_port(context, id, port)
def delete_port(self, context, id):
port_in_db = self.get_port(context, id)
plugin = self._get_plugin_by_network_id(port_in_db['network_id'])
return plugin.delete_port(context, id)
def create_subnet(self, context, subnet):
s = subnet['subnet']
if not 'network_id' in s:
raise exc.NotFound
plugin = self._get_plugin_by_network_id(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(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(s['network_id'])
return plugin.delete_subnet(context, id)