deb-murano/murano/common/plugins/extensions_loader.py
LiuNanke 0fe151d07f Fix string interpolation to delayed by logging
String interpolation should be delayed to be handled by the logging
code, rather than being done at the point of the logging call.

See the oslo i18n guideline.
* http://docs.openstack.org/developer/oslo.i18n/guidelines.html

References: https://review.openstack.org/#/c/339268

Change-Id: Ie4ea466f951db796fd85277c52be40018dfb01ac
Closes-Bug:#1596829
2016-08-02 18:42:01 +08:00

143 lines
5.5 KiB
Python

# Copyright (c) 2015 Mirantis, 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.
import inspect
import re
from oslo_config import cfg
from oslo_log import log as logging
import six
from stevedore import dispatch
from murano.common.i18n import _LE, _LI, _LW
from murano.dsl import murano_package
CONF = cfg.CONF
LOG = logging.getLogger(__name__)
# regexp validator to ensure that the entry-point name is a valid MuranoPL
# class name with an optional namespace name
NAME_RE = re.compile(r'^[a-zA-Z]\w*(\.[a-zA-Z]\w*)*$')
class PluginLoader(object):
def __init__(self, namespace="io.murano.extensions"):
LOG.info('Loading extension plugins')
self.namespace = namespace
extension_manager = dispatch.EnabledExtensionManager(
self.namespace,
PluginLoader.is_plugin_enabled,
on_load_failure_callback=PluginLoader._on_load_failure)
self.packages = {}
name_map = {}
for ext in extension_manager.extensions:
self.load_extension(ext, name_map)
self.cleanup_duplicates(name_map)
def load_extension(self, extension, name_map):
dist_name = str(extension.entry_point.dist)
name = extension.entry_point.name
if not NAME_RE.match(name):
LOG.warning(_LW("Entry-point 'name' {name} is invalid").format(
name=name))
return
name_map.setdefault(name, []).append(dist_name)
if dist_name in self.packages:
package = self.packages[dist_name]
else:
package = PackageDefinition(extension.entry_point.dist)
self.packages[dist_name] = package
plugin = extension.plugin
try:
package.classes[name] = initialize_plugin(plugin)
except Exception:
LOG.exception(_LE("Unable to initialize plugin for {name}").format(
name=name))
return
LOG.info(_LI("Loaded class {class_name} from {dist}").format(
class_name=name, dist=dist_name))
def cleanup_duplicates(self, name_map):
for class_name, package_names in six.iteritems(name_map):
if len(package_names) >= 2:
LOG.warning(_LW("Class is defined in multiple packages!"))
for package_name in package_names:
LOG.warning(_LW("Disabling class {class_name} in {dist} "
"due to conflict").format(
class_name=class_name,
dist=package_name))
self.packages[package_name].classes.pop(class_name)
@staticmethod
def is_plugin_enabled(extension):
if CONF.murano.enabled_plugins is None:
# assume all plugins are enabled until manually specified otherwise
return True
else:
return (extension.entry_point.dist.project_name in
CONF.murano.enabled_plugins)
@staticmethod
def _on_load_failure(manager, ep, exc):
LOG.warning(_LW("Error loading entry-point {ep} from package {dist}: "
"{err}").format(ep=ep.name, dist=ep.dist, err=exc))
def register_in_loader(self, package_loader):
for package in six.itervalues(self.packages):
package_loader.register_package(
MuranoPackage(package_loader, package))
def initialize_plugin(plugin):
if hasattr(plugin, "init_plugin"):
initializer = getattr(plugin, "init_plugin")
if inspect.ismethod(initializer) and initializer.__self__ is plugin:
LOG.debug("Initializing plugin class {name}".format(name=plugin))
initializer()
return plugin
class PackageDefinition(object):
def __init__(self, distribution):
self.name = distribution.project_name
self.version = distribution.version
if distribution.has_metadata(distribution.PKG_INFO):
# This has all the package metadata, including Author,
# description, License etc
self.info = distribution.get_metadata(distribution.PKG_INFO)
else:
self.info = None
self.classes = {}
class MuranoPackage(murano_package.MuranoPackage):
def __init__(self, pkg_loader, package_definition):
super(MuranoPackage, self).__init__(
pkg_loader, package_definition.name, runtime_version='1.0')
for class_name, clazz in six.iteritems(package_definition.classes):
if hasattr(clazz, "_murano_class_name"):
LOG.warning(_LW("Class '%(class_name)s' has a MuranoPL "
"name '%(name)s' defined which will be "
"ignored") %
dict(class_name=class_name,
name=getattr(clazz, "_murano_class_name")))
LOG.debug("Registering '%s' from '%s' in class loader",
class_name, package_definition.name)
self.register_class(clazz, class_name)
def get_resource(self, name):
raise NotImplementedError()