
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
217 lines
8.6 KiB
Python
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)
|