Use python logging in install / log cherrypy at right severity
[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 import logging
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), severity=logging.DEBUG)
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                                severity=logging.ERROR)
46             return
47
48         if hasattr(mod, class_type):
49             instance = getattr(mod, class_type)(*pargs)
50             public_name = getattr(instance, 'name', name)
51             tree[public_name] = instance
52             cherrypy.log.error('Added module %s as %s' % (name, public_name),
53                                severity=logging.DEBUG)
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                                severity=logging.ERROR)
62             return
63
64         for name in files:
65             filename = os.path.join(path, name)
66             self._load_class(tree, class_type, filename, *pargs)
67
68     def get_plugins(self, path, class_type, *pargs):
69         plugins = dict()
70         self._load_classes(plugins, path, class_type, *pargs)
71         return plugins
72
73
74 class PluginLoader(Log):
75
76     def __init__(self, baseobj, facility, plugin_type):
77         self._pathname, _ = os.path.split(inspect.getfile(baseobj))
78         self.facility = facility
79         self._plugin_type = plugin_type
80         self.available = dict()
81         self.enabled = list()
82         self.__data = None
83
84     # Defer initialization or instantiating the store will fail at load
85     # time when used with Installer plugins as the cherrypy config context
86     # is created after all Installer plugins are loaded.
87     @property
88     def _data(self):
89         if not self.__data:
90             self.__data = AdminStore()
91         return self.__data
92
93     @property
94     def is_readonly(self):
95         return self._data.is_readonly
96
97     def get_plugins(self):
98         p = Plugins()
99         return p.get_plugins(self._pathname, self._plugin_type, self)
100
101     def refresh_enabled(self):
102         config = self._data.load_options(self.facility, name='global')
103         self.enabled = []
104         if config:
105             if 'enabled' in config:
106                 self.enabled = config['enabled'].split(',')
107
108     def get_plugin_data(self):
109         self.available = self.get_plugins()
110         self.refresh_enabled()
111
112     def save_enabled(self, enabled):
113         if enabled:
114             self._data.save_options(self.facility, 'global',
115                                     {'enabled': ','.join(enabled)})
116         else:
117             self._data.delete_options(self.facility, 'global',
118                                       {'enabled': '*'})
119         self.debug('Plugin enabled state saved: %s' % enabled)
120         self.refresh_enabled()
121
122
123 class PluginInstaller(PluginLoader):
124     def __init__(self, baseobj, facility):
125         super(PluginInstaller, self).__init__(baseobj, facility, 'Installer')
126
127
128 class PluginObject(Log):
129
130     def __init__(self, plugins):
131         self.name = None
132         self._config = None
133         self._data = AdminStore()
134         self._plugins = plugins
135         self.is_enabled = False
136
137     @property
138     def is_readonly(self):
139         return self._data.is_readonly
140
141     def on_enable(self):
142         return
143
144     def on_disable(self):
145         return
146
147     def save_enabled_state(self):
148         enabled = []
149         self._plugins.refresh_enabled()
150         enabled.extend(self._plugins.enabled)
151         if self.is_enabled:
152             if self.name not in enabled:
153                 enabled.append(self.name)
154         else:
155             if self.name in enabled:
156                 enabled.remove(self.name)
157         self._plugins.save_enabled(enabled)
158
159     def enable(self):
160         if self.is_enabled:
161             return
162
163         self.refresh_plugin_config()
164         self.on_enable()
165         self.is_enabled = True
166         self.debug('Plugin enabled: %s' % self.name)
167
168     def disable(self):
169         if not self.is_enabled:
170             return
171
172         self.on_disable()
173
174         self.is_enabled = False
175         self.debug('Plugin disabled: %s' % self.name)
176
177     def import_config(self, config):
178         self._config = config
179
180     def export_config(self):
181         return self._config
182
183     def get_plugin_config(self):
184         return self._data.load_options(self._plugins.facility, self.name)
185
186     def refresh_plugin_config(self):
187         config = self.get_plugin_config()
188         if config:
189             try:
190                 self.import_config(config)
191             except Exception, e:  # pylint: disable=broad-except
192                 self.error('Failed to refresh config for %s (%s)' %
193                            (self.name, e))
194
195     def save_plugin_config(self, config=None):
196         if config is None:
197             config = self.export_config()
198
199         self._data.save_options(self._plugins.facility, self.name, config)
200
201     def get_data(self, idval=None, name=None, value=None):
202         return self._data.get_data(self.name, idval=idval, name=name,
203                                    value=value)
204
205     def save_data(self, data):
206         self._data.save_data(self.name, data)
207
208     def new_datum(self, datum):
209         self._data.new_datum(self.name, datum)
210
211     def del_datum(self, idval):
212         self._data.del_datum(self.name, idval)
213
214     def wipe_config_values(self):
215         self._data.delete_options(self._plugins.facility, self.name, None)
216
217     def wipe_data(self):
218         self._data.wipe_data(self.name)