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