Starting to move to new plugin architecture.
This commit is contained in:
16
lemur/plugins/base/__init__.py
Normal file
16
lemur/plugins/base/__init__.py
Normal file
@ -0,0 +1,16 @@
|
||||
"""
|
||||
.. module: lemur.plugins.base
|
||||
:platform: Unix
|
||||
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
|
||||
:license: Apache, see LICENSE for more details.
|
||||
|
||||
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
||||
"""
|
||||
from __future__ import absolute_import, print_function
|
||||
|
||||
from lemur.plugins.base.manager import PluginManager
|
||||
from lemur.plugins.base.v1 import * # NOQA
|
||||
|
||||
plugins = PluginManager()
|
||||
register = plugins.register
|
||||
unregister = plugins.unregister
|
59
lemur/plugins/base/manager.py
Normal file
59
lemur/plugins/base/manager.py
Normal file
@ -0,0 +1,59 @@
|
||||
"""
|
||||
.. module: lemur.plugins.base.manager
|
||||
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
|
||||
:license: Apache, see LICENSE for more details.
|
||||
|
||||
.. moduleauthor:: Kevin Glisson (kglisson@netflix.com)
|
||||
"""
|
||||
from flask import current_app
|
||||
from lemur.common.managers import InstanceManager
|
||||
|
||||
|
||||
# inspired by https://github.com/getsentry/sentry
|
||||
class PluginManager(InstanceManager):
|
||||
def __iter__(self):
|
||||
return iter(self.all())
|
||||
|
||||
def __len__(self):
|
||||
return sum(1 for i in self.all())
|
||||
|
||||
def all(self, version=1):
|
||||
for plugin in sorted(super(PluginManager, self).all(), key=lambda x: x.get_title()):
|
||||
if not plugin.is_enabled():
|
||||
continue
|
||||
if version is not None and plugin.__version__ != version:
|
||||
continue
|
||||
yield plugin
|
||||
|
||||
def get(self, slug):
|
||||
for plugin in self.all(version=1):
|
||||
if plugin.slug == slug:
|
||||
return plugin
|
||||
for plugin in self.all(version=2):
|
||||
if plugin.slug == slug:
|
||||
return plugin
|
||||
raise KeyError(slug)
|
||||
|
||||
def first(self, func_name, *args, **kwargs):
|
||||
version = kwargs.pop('version', 1)
|
||||
for plugin in self.all(version=version):
|
||||
try:
|
||||
result = getattr(plugin, func_name)(*args, **kwargs)
|
||||
except Exception as e:
|
||||
current_app.logger.error('Error processing %s() on %r: %s', func_name, plugin.__class__, e, extra={
|
||||
'func_arg': args,
|
||||
'func_kwargs': kwargs,
|
||||
}, exc_info=True)
|
||||
continue
|
||||
|
||||
if result is not None:
|
||||
return result
|
||||
|
||||
def register(self, cls):
|
||||
self.add('%s.%s' % (cls.__module__, cls.__name__))
|
||||
return cls
|
||||
|
||||
def unregister(self, cls):
|
||||
self.remove('%s.%s' % (cls.__module__, cls.__name__))
|
||||
return cls
|
||||
|
117
lemur/plugins/base/v1.py
Normal file
117
lemur/plugins/base/v1.py
Normal file
@ -0,0 +1,117 @@
|
||||
"""
|
||||
.. module: lemur.plugins.base.v1
|
||||
:platform: Unix
|
||||
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
|
||||
:license: Apache, see LICENSE for more details.
|
||||
|
||||
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
||||
"""
|
||||
from threading import local
|
||||
|
||||
# stolen from https://github.com/getsentry/sentry/
|
||||
class PluginMount(type):
|
||||
def __new__(cls, name, bases, attrs):
|
||||
new_cls = type.__new__(cls, name, bases, attrs)
|
||||
if IPlugin in bases:
|
||||
return new_cls
|
||||
if new_cls.title is None:
|
||||
new_cls.title = new_cls.__name__
|
||||
if not new_cls.slug:
|
||||
new_cls.slug = new_cls.title.replace(' ', '-').lower()
|
||||
return new_cls
|
||||
|
||||
|
||||
class IPlugin(local):
|
||||
"""
|
||||
Plugin interface. Should not be inherited from directly.
|
||||
A plugin should be treated as if it were a singleton. The owner does not
|
||||
control when or how the plugin gets instantiated, nor is it guaranteed that
|
||||
it will happen, or happen more than once.
|
||||
>>> from lemur.plugins import Plugin
|
||||
>>>
|
||||
>>> class MyPlugin(Plugin):
|
||||
>>> def get_title(self):
|
||||
>>> return 'My Plugin'
|
||||
As a general rule all inherited methods should allow ``**kwargs`` to ensure
|
||||
ease of future compatibility.
|
||||
"""
|
||||
# Generic plugin information
|
||||
title = None
|
||||
slug = None
|
||||
description = None
|
||||
version = None
|
||||
author = None
|
||||
author_url = None
|
||||
resource_links = ()
|
||||
|
||||
# Configuration specifics
|
||||
conf_key = None
|
||||
conf_title = None
|
||||
|
||||
# Global enabled state
|
||||
enabled = True
|
||||
can_disable = True
|
||||
|
||||
def is_enabled(self, project=None):
|
||||
"""
|
||||
Returns a boolean representing if this plugin is enabled.
|
||||
If ``project`` is passed, it will limit the scope to that project.
|
||||
>>> plugin.is_enabled()
|
||||
"""
|
||||
if not self.enabled:
|
||||
return False
|
||||
if not self.can_disable:
|
||||
return True
|
||||
|
||||
return True
|
||||
|
||||
def get_conf_key(self):
|
||||
"""
|
||||
Returns a string representing the configuration keyspace prefix for this plugin.
|
||||
"""
|
||||
if not self.conf_key:
|
||||
self.conf_key = self.get_conf_title().lower().replace(' ', '_')
|
||||
return self.conf_key
|
||||
|
||||
def get_conf_title(self):
|
||||
"""
|
||||
Returns a string representing the title to be shown on the configuration page.
|
||||
"""
|
||||
return self.conf_title or self.get_title()
|
||||
|
||||
def get_title(self):
|
||||
"""
|
||||
Returns the general title for this plugin.
|
||||
>>> plugin.get_title()
|
||||
"""
|
||||
return self.title
|
||||
|
||||
def get_description(self):
|
||||
"""
|
||||
Returns the description for this plugin. This is shown on the plugin configuration
|
||||
page.
|
||||
>>> plugin.get_description()
|
||||
"""
|
||||
return self.description
|
||||
|
||||
def get_resource_links(self):
|
||||
"""
|
||||
Returns a list of tuples pointing to various resources for this plugin.
|
||||
>>> def get_resource_links(self):
|
||||
>>> return [
|
||||
>>> ('Documentation', 'http://sentry.readthedocs.org'),
|
||||
>>> ('Bug Tracker', 'https://github.com/getsentry/sentry/issues'),
|
||||
>>> ('Source', 'https://github.com/getsentry/sentry'),
|
||||
>>> ]
|
||||
"""
|
||||
return self.resource_links
|
||||
|
||||
|
||||
class Plugin(IPlugin):
|
||||
"""
|
||||
A plugin should be treated as if it were a singleton. The owner does not
|
||||
control when or how the plugin gets instantiated, nor is it guaranteed that
|
||||
it will happen, or happen more than once.
|
||||
"""
|
||||
__version__ = 1
|
||||
__metaclass__ = PluginMount
|
Reference in New Issue
Block a user