summaryrefslogtreecommitdiffstats
path: root/src/pybind/mgr/dashboard/plugins/__init__.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/pybind/mgr/dashboard/plugins/__init__.py')
-rw-r--r--src/pybind/mgr/dashboard/plugins/__init__.py71
1 files changed, 71 insertions, 0 deletions
diff --git a/src/pybind/mgr/dashboard/plugins/__init__.py b/src/pybind/mgr/dashboard/plugins/__init__.py
new file mode 100644
index 000000000..edc0b6594
--- /dev/null
+++ b/src/pybind/mgr/dashboard/plugins/__init__.py
@@ -0,0 +1,71 @@
+# -*- coding: utf-8 -*-
+
+import abc
+
+from .pluggy import HookimplMarker, HookspecMarker, PluginManager
+
+
+class Interface(object, metaclass=abc.ABCMeta):
+ pass
+
+
+class Mixin(object):
+ pass
+
+
+class DashboardPluginManager(object):
+ def __init__(self, project_name):
+ self.__pm = PluginManager(project_name)
+ self.__add_spec = HookspecMarker(project_name)
+ self.__add_abcspec = lambda *args, **kwargs: abc.abstractmethod(
+ self.__add_spec(*args, **kwargs))
+ self.__add_hook = HookimplMarker(project_name)
+
+ pm = property(lambda self: self.__pm)
+ hook = property(lambda self: self.pm.hook)
+
+ add_spec = property(lambda self: self.__add_spec)
+ add_abcspec = property(lambda self: self.__add_abcspec)
+ add_hook = property(lambda self: self.__add_hook)
+
+ def add_interface(self, cls):
+ assert issubclass(cls, Interface)
+ self.pm.add_hookspecs(cls)
+ return cls
+
+ @staticmethod
+ def final(func):
+ setattr(func, '__final__', True)
+ return func
+
+ def add_plugin(self, plugin):
+ """ Provides decorator interface for PluginManager.register():
+ @PLUGIN_MANAGER.add_plugin
+ class Plugin(...):
+ ...
+ Additionally it checks whether the Plugin instance has all Interface
+ methods implemented and marked with add_hook decorator.
+ As a con of this approach, plugins cannot call super() from __init__()
+ """
+ assert issubclass(plugin, Interface)
+ from inspect import getmembers, ismethod
+ for interface in plugin.__bases__:
+ for method_name, _ in getmembers(interface, predicate=ismethod):
+ if hasattr(getattr(interface, method_name), '__final__'):
+ continue
+
+ if self.pm.parse_hookimpl_opts(plugin, method_name) is None:
+ raise NotImplementedError(
+ "Plugin '{}' implements interface '{}' but existing"
+ " method '{}' is not declared added as hook".format(
+ plugin.__name__,
+ interface.__name__,
+ method_name))
+ self.pm.register(plugin())
+ return plugin
+
+
+PLUGIN_MANAGER = DashboardPluginManager("ceph-mgr.dashboard")
+
+# Load all interfaces and their hooks
+from . import interfaces # noqa pylint: disable=C0413,W0406