-#!/usr/bin/python
-#
-# Copyright (C) 2013 Simo Sorce <simo@redhat.com>
-#
-# see file 'COPYING' for use and warranty information
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# Copyright (C) 2013 Ipsilon project Contributors, for license see COPYING
import os
import imp
import cherrypy
import inspect
-from ipsilon.util.data import AdminStore
+import logging
+from ipsilon.util.data import AdminStore, Store
from ipsilon.util.log import Log
def __init__(self):
self._providers_tree = None
- def _load_class(self, tree, class_type, file_name):
+ def _load_class(self, tree, class_type, file_name, *pargs):
cherrypy.log.error('Check module %s for class %s' % (file_name,
- class_type))
+ class_type), severity=logging.DEBUG)
name, ext = os.path.splitext(os.path.split(file_name)[-1])
try:
if ext.lower() == '.py':
else:
return
except Exception, e: # pylint: disable=broad-except
- cherrypy.log.error('Failed to load "%s" module: [%s]' % (name, e))
+ cherrypy.log.error('Failed to load "%s" module: [%s]' % (name, e),
+ severity=logging.ERROR)
return
if hasattr(mod, class_type):
- instance = getattr(mod, class_type)()
+ instance = getattr(mod, class_type)(*pargs)
public_name = getattr(instance, 'name', name)
tree[public_name] = instance
- cherrypy.log.error('Added module %s as %s' % (name, public_name))
+ cherrypy.log.error('Added module %s as %s' % (name, public_name),
+ severity=logging.DEBUG)
- def _load_classes(self, tree, path, class_type):
+ def _load_classes(self, tree, path, class_type, *pargs):
files = None
try:
files = os.listdir(path)
except Exception, e: # pylint: disable=broad-except
- cherrypy.log.error('No modules in %s: [%s]' % (path, e))
+ cherrypy.log.error('No modules in %s: [%s]' % (path, e),
+ severity=logging.ERROR)
return
for name in files:
filename = os.path.join(path, name)
- self._load_class(tree, class_type, filename)
+ self._load_class(tree, class_type, filename, *pargs)
- def get_plugins(self, path, class_type):
+ def get_plugins(self, path, class_type, *pargs):
plugins = dict()
- self._load_classes(plugins, path, class_type)
+ self._load_classes(plugins, path, class_type, *pargs)
return plugins
-class PluginLoader(object):
-
- def __init__(self, baseobj, facility, plugin_type):
- config = AdminStore().load_options(facility)
- cherrypy.log('LOAD: %s\n' % repr(config))
- whitelist = []
- if 'global' in config:
- sec = config['global']
- if 'order' in sec:
- whitelist = sec['order'].split(',')
- if cherrypy.config.get('debug', False):
- cherrypy.log('[%s] %s: %s' % (facility, whitelist, config))
- if config is None:
- config = dict()
+class PluginLoader(Log):
+
+ def __init__(self, baseobj, facility, plugin_type, uses_store=True):
+ self._pathname, _ = os.path.split(inspect.getfile(baseobj))
+ self.facility = facility
+ self._plugin_type = plugin_type
+ self.available = dict()
+ self.enabled = list()
+ self.__data = False
+ self.uses_store = uses_store
+
+ # Defer initialization or instantiating the store will fail at load
+ # time when used with Installer plugins as the cherrypy config context
+ # is created after all Installer plugins are loaded.
+ @property
+ def _data(self):
+ if not self.uses_store:
+ raise Exception('Tried to get plugin data while ' +
+ 'uses_store=False (%s)' % self.facility)
+ if not self.__data:
+ self.__data = AdminStore()
+ return self.__data
+
+ @property
+ def is_readonly(self):
+ return self._data.is_readonly
+ def get_plugins(self):
p = Plugins()
- (pathname, dummy) = os.path.split(inspect.getfile(baseobj))
- self._plugins = {
- 'config': config,
- 'available': p.get_plugins(pathname, plugin_type),
- 'whitelist': whitelist,
- 'enabled': []
- }
+ return p.get_plugins(self._pathname, self._plugin_type, self)
+
+ def refresh_enabled(self):
+ config = self._data.load_options(self.facility, name='global')
+ self.enabled = []
+ if config:
+ if 'enabled' in config:
+ self.enabled = config['enabled'].split(',')
def get_plugin_data(self):
- return self._plugins
+ self.available = self.get_plugins()
+ if self.uses_store:
+ self.refresh_enabled()
+ def save_enabled(self, enabled):
+ if enabled:
+ self._data.save_options(self.facility, 'global',
+ {'enabled': ','.join(enabled)})
+ else:
+ self._data.delete_options(self.facility, 'global',
+ {'enabled': '*'})
+ self.debug('Plugin enabled state saved: %s' % enabled)
+ self.refresh_enabled()
-class PluginInstaller(object):
- def __init__(self, baseobj):
- (pathname, dummy) = os.path.split(inspect.getfile(baseobj))
- self._pathname = pathname
- def get_plugins(self):
- p = Plugins()
- return p.get_plugins(self._pathname, 'Installer')
+class PluginInstaller(PluginLoader):
+ def __init__(self, baseobj, facility):
+ super(PluginInstaller, self).__init__(baseobj, facility, 'Installer')
class PluginObject(Log):
- def __init__(self):
+ def __init__(self, plugins):
self.name = None
self._config = None
- self._options = None
self._data = AdminStore()
+ self._plugins = plugins
+ self.is_enabled = False
+
+ @property
+ def is_readonly(self):
+ return self._data.is_readonly
+
+ def on_enable(self):
+ return
+
+ def on_disable(self):
+ return
+
+ def save_enabled_state(self):
+ enabled = []
+ self._plugins.refresh_enabled()
+ enabled.extend(self._plugins.enabled)
+ if self.is_enabled:
+ if self.name not in enabled:
+ enabled.append(self.name)
+ else:
+ if self.name in enabled:
+ enabled.remove(self.name)
+ self._plugins.save_enabled(enabled)
+
+ def enable(self):
+ if self.is_enabled:
+ return
- def get_config_desc(self, name=None):
- """ The configuration description is a dictionary that provides
- A description of the supported configuration options, as well
- as the default configuration option values.
- The key is the option name, the value is an array of 3 elements:
- - description
- - option type
- - default value
- """
- if name is None:
- return self._options
-
- opt = self._options.get(name, None)
- if opt is None:
- return ''
- return opt[0]
-
- def set_config(self, config):
- self._config = config
+ self.refresh_plugin_config()
+ is_upgrade = Store._is_upgrade # pylint: disable=protected-access
+ try:
+ Store._is_upgrade = True # pylint: disable=protected-access
+ self.on_enable()
+ self._data.create_plugin_data_table(self.name)
+ for store in self.used_datastores():
+ store.upgrade_database()
+ finally:
+ Store._is_upgrade = is_upgrade # pylint: disable=protected-access
+ self.is_enabled = True
+ self.debug('Plugin enabled: %s' % self.name)
+
+ def disable(self):
+ if not self.is_enabled:
+ return
- def get_config_value(self, name):
- value = None
- if self._config:
- value = self._config.get(name, None)
- if not value:
- if self._options:
- opt = self._options.get(name, None)
- if opt:
- value = opt[2]
+ self.on_disable()
- if cherrypy.config.get('debug', False):
- cherrypy.log('[%s] %s: %s' % (self.name, name, value))
+ self.is_enabled = False
+ self.debug('Plugin disabled: %s' % self.name)
- return value
+ def used_datastores(self):
+ return []
- def set_config_value(self, option, value):
- if not self._config:
- self._config = dict()
- self._config[option] = value
+ def import_config(self, config):
+ self._config = config
+
+ def export_config(self):
+ return self._config
- def get_plugin_config(self, facility):
- return self._data.load_options(facility, self.name)
+ def get_plugin_config(self):
+ return self._data.load_options(self._plugins.facility, self.name)
- def refresh_plugin_config(self, facility):
- config = self.get_plugin_config(facility)
- self.set_config(config)
+ def refresh_plugin_config(self):
+ config = self.get_plugin_config()
+ if config:
+ try:
+ self.import_config(config)
+ except Exception, e: # pylint: disable=broad-except
+ self.error('Failed to refresh config for %s (%s)' %
+ (self.name, e))
- def save_plugin_config(self, facility, config=None):
+ def save_plugin_config(self, config=None):
if config is None:
- config = self._config
- self._data.save_options(facility, self.name, config)
+ config = self.export_config()
+
+ self._data.save_options(self._plugins.facility, self.name, config)
def get_data(self, idval=None, name=None, value=None):
return self._data.get_data(self.name, idval=idval, name=name,
def del_datum(self, idval):
self._data.del_datum(self.name, idval)
- def wipe_config_values(self, facility):
- self._data.delete_options(facility, self.name, None)
+ def wipe_config_values(self):
+ self._data.delete_options(self._plugins.facility, self.name, None)
def wipe_data(self):
self._data.wipe_data(self.name)