bacff4b225f619db26960e7610548929fe9147d9
[cascardo/ipsilon.git] / ipsilon / util / plugin.py
1 # Copyright (C) 2013  Simo Sorce <simo@redhat.com>
2 #
3 # see file 'COPYING' for use and warranty information
4 #
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18 import os
19 import imp
20 import cherrypy
21 import inspect
22 from ipsilon.util.config import Config
23 from ipsilon.util.data import AdminStore
24 from ipsilon.util.log import Log
25
26
27 class Plugins(object):
28
29     def __init__(self):
30         self._providers_tree = None
31
32     def _load_class(self, tree, class_type, file_name, *pargs):
33         cherrypy.log.error('Check module %s for class %s' % (file_name,
34                                                              class_type))
35         name, ext = os.path.splitext(os.path.split(file_name)[-1])
36         try:
37             if ext.lower() == '.py':
38                 mod = imp.load_source(name, file_name)
39             # elif ext.lower() == '.pyc':
40             #    mod = imp.load_compiled(name, file_name)
41             else:
42                 return
43         except Exception, e:  # pylint: disable=broad-except
44             cherrypy.log.error('Failed to load "%s" module: [%s]' % (name, e))
45             return
46
47         if hasattr(mod, class_type):
48             instance = getattr(mod, class_type)(*pargs)
49             public_name = getattr(instance, 'name', name)
50             tree[public_name] = instance
51             cherrypy.log.error('Added module %s as %s' % (name, public_name))
52
53     def _load_classes(self, tree, path, class_type, *pargs):
54         files = None
55         try:
56             files = os.listdir(path)
57         except Exception, e:  # pylint: disable=broad-except
58             cherrypy.log.error('No modules in %s: [%s]' % (path, e))
59             return
60
61         for name in files:
62             filename = os.path.join(path, name)
63             self._load_class(tree, class_type, filename, *pargs)
64
65     def get_plugins(self, path, class_type, *pargs):
66         plugins = dict()
67         self._load_classes(plugins, path, class_type, *pargs)
68         return plugins
69
70
71 class PluginLoader(Log):
72
73     def __init__(self, baseobj, facility, plugin_type):
74         self._pathname, _ = os.path.split(inspect.getfile(baseobj))
75         self.facility = facility
76         self._plugin_type = plugin_type
77         self.available = dict()
78         self.enabled = list()
79         self.__data = None
80
81     # Defer initialization or instantiating the store will fail at load
82     # time when used with Installer plugins as the cherrypy config context
83     # is created after all Installer plugins are loaded.
84     @property
85     def _data(self):
86         if not self.__data:
87             self.__data = AdminStore()
88         return self.__data
89
90     @property
91     def is_readonly(self):
92         return self._data.is_readonly
93
94     def get_plugins(self):
95         p = Plugins()
96         return p.get_plugins(self._pathname, self._plugin_type, self)
97
98     def refresh_enabled(self):
99         config = self._data.load_options(self.facility, name='global')
100         self.enabled = []
101         if config:
102             if 'enabled' in config:
103                 self.enabled = config['enabled'].split(',')
104
105     def get_plugin_data(self):
106         self.available = self.get_plugins()
107         self.refresh_enabled()
108
109     def save_enabled(self, enabled):
110         if enabled:
111             self._data.save_options(self.facility, 'global',
112                                     {'enabled': ','.join(enabled)})
113         else:
114             self._data.delete_options(self.facility, 'global',
115                                       {'enabled': '*'})
116         self.debug('Plugin enabled state saved: %s' % enabled)
117         self.refresh_enabled()
118
119
120 class PluginInstaller(PluginLoader):
121     def __init__(self, baseobj, facility):
122         super(PluginInstaller, self).__init__(baseobj, facility, 'Installer')
123
124
125 class PluginObject(Log):
126
127     def __init__(self, plugins):
128         self.name = None
129         self._config = None
130         self._data = AdminStore()
131         self._plugins = plugins
132         self.is_enabled = False
133
134     @property
135     def is_readonly(self):
136         return self._data.is_readonly
137
138     def on_enable(self):
139         return
140
141     def on_disable(self):
142         return
143
144     def save_enabled_state(self):
145         enabled = []
146         self._plugins.refresh_enabled()
147         enabled.extend(self._plugins.enabled)
148         if self.is_enabled:
149             if self.name not in enabled:
150                 enabled.append(self.name)
151         else:
152             if self.name in enabled:
153                 enabled.remove(self.name)
154         self._plugins.save_enabled(enabled)
155
156     def enable(self):
157         if self.is_enabled:
158             return
159
160         self.refresh_plugin_config()
161         self.on_enable()
162         self.is_enabled = True
163         self.debug('Plugin enabled: %s' % self.name)
164
165     def disable(self):
166         if not self.is_enabled:
167             return
168
169         self.on_disable()
170
171         self.is_enabled = False
172         self.debug('Plugin disabled: %s' % self.name)
173
174     def import_config(self, config):
175         self._config = config
176
177     def export_config(self):
178         return self._config
179
180     def get_plugin_config(self):
181         return self._data.load_options(self._plugins.facility, self.name)
182
183     def refresh_plugin_config(self):
184         config = self.get_plugin_config()
185         if config:
186             self.import_config(config)
187
188     def save_plugin_config(self, config=None):
189         if config is None:
190             config = self.export_config()
191
192         self._data.save_options(self._plugins.facility, self.name, config)
193
194     def get_data(self, idval=None, name=None, value=None):
195         return self._data.get_data(self.name, idval=idval, name=name,
196                                    value=value)
197
198     def save_data(self, data):
199         self._data.save_data(self.name, data)
200
201     def new_datum(self, datum):
202         self._data.new_datum(self.name, datum)
203
204     def del_datum(self, idval):
205         self._data.del_datum(self.name, idval)
206
207     def wipe_config_values(self):
208         self._data.delete_options(self._plugins.facility, self.name, None)
209
210     def wipe_data(self):
211         self._data.wipe_data(self.name)
212
213
214 class PluginConfig(Log):
215
216     def __init__(self):
217         self._config = None
218
219     def new_config(self, name, *config_args):
220         self._config = Config(name, *config_args)
221
222     def get_config_obj(self):
223         if self._config is None:
224             raise AttributeError('Config not initialized')
225         return self._config
226
227     def import_config(self, config):
228         if not self._config:
229             raise AttributeError('Config not initialized, cannot import')
230
231         for key, value in config.iteritems():
232             if key in self._config:
233                 self._config[key].import_value(str(value))
234
235     def export_config(self):
236         config = dict()
237         for name, option in self._config.iteritems():
238             config[name] = option.export_value()
239         return config
240
241     def get_config_value(self, name):
242         if not self._config:
243             raise AttributeError('Config not initialized')
244         return self._config[name].get_value()
245
246     def set_config_value(self, name, value):
247         if not self._config:
248             raise AttributeError('Config not initialized')
249         return self._config[name].set_value(value)