1f66d8ebb4af6193a79165acfa8c20935f408ea7
[cascardo/ipsilon.git] / ipsilon / util / plugin.py
1 # Copyright (C) 2013 Ipsilon project Contributors, for license see COPYING
2
3 import os
4 import imp
5 import cherrypy
6 import inspect
7 import logging
8 from ipsilon.util.data import AdminStore
9 from ipsilon.util.log import Log
10
11
12 class Plugins(object):
13
14     def __init__(self):
15         self._providers_tree = None
16
17     def _load_class(self, tree, class_type, file_name, *pargs):
18         cherrypy.log.error('Check module %s for class %s' % (file_name,
19                            class_type), severity=logging.DEBUG)
20         name, ext = os.path.splitext(os.path.split(file_name)[-1])
21         try:
22             if ext.lower() == '.py':
23                 mod = imp.load_source(name, file_name)
24             # elif ext.lower() == '.pyc':
25             #    mod = imp.load_compiled(name, file_name)
26             else:
27                 return
28         except Exception, e:  # pylint: disable=broad-except
29             cherrypy.log.error('Failed to load "%s" module: [%s]' % (name, e),
30                                severity=logging.ERROR)
31             return
32
33         if hasattr(mod, class_type):
34             instance = getattr(mod, class_type)(*pargs)
35             public_name = getattr(instance, 'name', name)
36             tree[public_name] = instance
37             cherrypy.log.error('Added module %s as %s' % (name, public_name),
38                                severity=logging.DEBUG)
39
40     def _load_classes(self, tree, path, class_type, *pargs):
41         files = None
42         try:
43             files = os.listdir(path)
44         except Exception, e:  # pylint: disable=broad-except
45             cherrypy.log.error('No modules in %s: [%s]' % (path, e),
46                                severity=logging.ERROR)
47             return
48
49         for name in files:
50             filename = os.path.join(path, name)
51             self._load_class(tree, class_type, filename, *pargs)
52
53     def get_plugins(self, path, class_type, *pargs):
54         plugins = dict()
55         self._load_classes(plugins, path, class_type, *pargs)
56         return plugins
57
58
59 class PluginLoader(Log):
60
61     def __init__(self, baseobj, facility, plugin_type):
62         self._pathname, _ = os.path.split(inspect.getfile(baseobj))
63         self.facility = facility
64         self._plugin_type = plugin_type
65         self.available = dict()
66         self.enabled = list()
67         self.__data = None
68
69     # Defer initialization or instantiating the store will fail at load
70     # time when used with Installer plugins as the cherrypy config context
71     # is created after all Installer plugins are loaded.
72     @property
73     def _data(self):
74         if not self.__data:
75             self.__data = AdminStore()
76         return self.__data
77
78     @property
79     def is_readonly(self):
80         return self._data.is_readonly
81
82     def get_plugins(self):
83         p = Plugins()
84         return p.get_plugins(self._pathname, self._plugin_type, self)
85
86     def refresh_enabled(self):
87         config = self._data.load_options(self.facility, name='global')
88         self.enabled = []
89         if config:
90             if 'enabled' in config:
91                 self.enabled = config['enabled'].split(',')
92
93     def get_plugin_data(self):
94         self.available = self.get_plugins()
95         self.refresh_enabled()
96
97     def save_enabled(self, enabled):
98         if enabled:
99             self._data.save_options(self.facility, 'global',
100                                     {'enabled': ','.join(enabled)})
101         else:
102             self._data.delete_options(self.facility, 'global',
103                                       {'enabled': '*'})
104         self.debug('Plugin enabled state saved: %s' % enabled)
105         self.refresh_enabled()
106
107
108 class PluginInstaller(PluginLoader):
109     def __init__(self, baseobj, facility):
110         super(PluginInstaller, self).__init__(baseobj, facility, 'Installer')
111
112
113 class PluginObject(Log):
114
115     def __init__(self, plugins):
116         self.name = None
117         self._config = None
118         self._data = AdminStore()
119         self._plugins = plugins
120         self.is_enabled = False
121
122     @property
123     def is_readonly(self):
124         return self._data.is_readonly
125
126     def on_enable(self):
127         return
128
129     def on_disable(self):
130         return
131
132     def save_enabled_state(self):
133         enabled = []
134         self._plugins.refresh_enabled()
135         enabled.extend(self._plugins.enabled)
136         if self.is_enabled:
137             if self.name not in enabled:
138                 enabled.append(self.name)
139         else:
140             if self.name in enabled:
141                 enabled.remove(self.name)
142         self._plugins.save_enabled(enabled)
143
144     def enable(self):
145         if self.is_enabled:
146             return
147
148         self.refresh_plugin_config()
149         self.on_enable()
150         self.is_enabled = True
151         self.debug('Plugin enabled: %s' % self.name)
152
153     def disable(self):
154         if not self.is_enabled:
155             return
156
157         self.on_disable()
158
159         self.is_enabled = False
160         self.debug('Plugin disabled: %s' % self.name)
161
162     def import_config(self, config):
163         self._config = config
164
165     def export_config(self):
166         return self._config
167
168     def get_plugin_config(self):
169         return self._data.load_options(self._plugins.facility, self.name)
170
171     def refresh_plugin_config(self):
172         config = self.get_plugin_config()
173         if config:
174             try:
175                 self.import_config(config)
176             except Exception, e:  # pylint: disable=broad-except
177                 self.error('Failed to refresh config for %s (%s)' %
178                            (self.name, e))
179
180     def save_plugin_config(self, config=None):
181         if config is None:
182             config = self.export_config()
183
184         self._data.save_options(self._plugins.facility, self.name, config)
185
186     def get_data(self, idval=None, name=None, value=None):
187         return self._data.get_data(self.name, idval=idval, name=name,
188                                    value=value)
189
190     def save_data(self, data):
191         self._data.save_data(self.name, data)
192
193     def new_datum(self, datum):
194         self._data.new_datum(self.name, datum)
195
196     def del_datum(self, idval):
197         self._data.del_datum(self.name, idval)
198
199     def wipe_config_values(self):
200         self._data.delete_options(self._plugins.facility, self.name, None)
201
202     def wipe_data(self):
203         self._data.wipe_data(self.name)