From 2b9722213d76306fe6f3b9f2fe157b3459d28e3a Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Fri, 24 Jan 2014 17:01:35 -0500 Subject: [PATCH] Better infrastructure to load plugins Signed-off-by: Simo Sorce --- examples/ipsilon.conf | 2 + ipsilon/idpserver.py | 6 --- ipsilon/util/data.py | 103 +++++++++++++++++++++++++++++++++++------ ipsilon/util/plugin.py | 93 ++++++++++++++++++++++++++++++------- 4 files changed, 167 insertions(+), 37 deletions(-) diff --git a/examples/ipsilon.conf b/examples/ipsilon.conf index fa75839..0f8e445 100644 --- a/examples/ipsilon.conf +++ b/examples/ipsilon.conf @@ -1,4 +1,6 @@ [global] +debug = True + log.screen = True base.mount = "/idp" base.dir = "../" diff --git a/ipsilon/idpserver.py b/ipsilon/idpserver.py index e260043..41a2cf4 100755 --- a/ipsilon/idpserver.py +++ b/ipsilon/idpserver.py @@ -23,7 +23,6 @@ sys.stdout = sys.stderr import os import atexit import cherrypy -from ipsilon.util.plugin import Plugins from ipsilon.util.data import Store from ipsilon.util import page from ipsilon.root import Root @@ -31,11 +30,6 @@ from jinja2 import Environment, FileSystemLoader cherrypy.config.update('ipsilon.conf') -plugins = Plugins(path=cherrypy.config['base.dir']) -idp_providers = plugins.get_providers() -if idp_providers: - cherrypy.config['idp_providers'] = idp_providers - datastore = Store() admin_config = datastore.get_admin_config() for option in admin_config: diff --git a/ipsilon/util/data.py b/ipsilon/util/data.py index 15bf5b7..251c998 100755 --- a/ipsilon/util/data.py +++ b/ipsilon/util/data.py @@ -29,6 +29,24 @@ class Store(object): self._path = os.getcwd() else: self._path = path + self._admin_dbname = self._get_admin_dbname() + self._user_dbname = self._get_userprefs_dbname() + + def _get_admin_dbname(self): + path = None + if 'admin.config.db' in cherrypy.config: + path = cherrypy.config['admin.config.db'] + if not path: + path = os.path.join(self._path, 'adminconfig.sqlite') + return path + + def _get_userprefs_dbname(self): + path = None + if 'user.prefs.db' in cherrypy.config: + path = cherrypy.config['user.prefs.db'] + if not path: + path = os.path.join(self._path, 'userprefs.sqlite') + return path def _load_config(self, dbname): con = None @@ -65,13 +83,7 @@ class Store(object): return conf def get_admin_config(self): - path = None - if 'admin.config.db' in cherrypy.config: - path = cherrypy.config['admin.config.db'] - if not path: - path = os.path.join(self._path, 'adminconfig.sqlite') - - return self._load_config(path) + return self._load_config(self._admin_dbname) def _load_user_prefs(self, dbname, user): con = None @@ -104,10 +116,75 @@ class Store(object): return conf def get_user_preferences(self, user): - path = None - if 'user.prefs.db' in cherrypy.config: - path = cherrypy.config['user.prefs.db'] - if not path: - path = os.path.join(self._path, 'userprefs.sqlite') + return self._load_user_prefs(self._user_dbname, user) - return self._load_user_prefs(path, user) + def get_plugins_config(self, facility): + con = None + rows = [] + try: + con = sqlite3.connect(self._admin_dbname) + cur = con.cursor() + cur.execute("CREATE TABLE IF NOT EXISTS " + + facility + " (name TEXT,option TEXT,value TEXT)") + cur.execute("SELECT * FROM " + facility) + rows = cur.fetchall() + con.commit() + except sqlite3.Error, e: + if con: + con.rollback() + cherrypy.log.error("Failed to load %s config: [%s]" % (facility, + e)) + finally: + if con: + con.close() + + lpo = [] + plco = dict() + for row in rows: + if row[0] == 'global': + if row[1] == 'order': + lpo = row[2].split(',') + continue + if row[0] not in plco: + # one dict per provider + plco[row[0]] = dict() + + conf = plco[row[0]] + if row[1] in conf: + if conf[row[1]] is list: + conf[row[1]].append(row[2]) + else: + v = conf[row[1]] + conf[row[1]] = [v, row[2]] + else: + conf[row[1]] = row[2] + + return (lpo, plco) + + def save_plugin_config(self, facility, plugin, options): + SELECT = "SELECT option, value FROM %s WHERE name=?" % facility + UPDATE = "UPDATE %s SET value=? WHERE name=? AND option=?" % facility + INSERT = "INSERT INTO %s VALUES(?,?,?)" % facility + con = None + try: + con = sqlite3.connect(self._admin_dbname) + cur = con.cursor() + curvals = dict() + for row in cur.execute(SELECT, (plugin,)): + curvals[row[0]] = row[1] + + for name in options: + if name in curvals: + cur.execute(UPDATE, (options[name], plugin, name)) + else: + cur.execute(INSERT, (plugin, name, options[name])) + + con.commit() + except sqlite3.Error, e: + if con: + con.rollback() + cherrypy.log.error("Failed to store config: [%s]" % e) + raise + finally: + if con: + con.close() diff --git a/ipsilon/util/plugin.py b/ipsilon/util/plugin.py index 917f28b..2077023 100755 --- a/ipsilon/util/plugin.py +++ b/ipsilon/util/plugin.py @@ -20,6 +20,8 @@ import os import imp import cherrypy +import inspect +from ipsilon.util.data import Store class Plugins(object): @@ -38,8 +40,8 @@ class Plugins(object): try: if ext.lower() == '.py': mod = imp.load_source(name, file_name) - elif ext.lower() == '.pyc': - mod = imp.load_compiled(name, file_name) + #elif ext.lower() == '.pyc': + # mod = imp.load_compiled(name, file_name) else: return except Exception, e: # pylint: disable=broad-except @@ -47,8 +49,10 @@ class Plugins(object): return if hasattr(mod, class_type): - tree[name] = getattr(mod, class_type)() - cherrypy.log.error('Added module %s' % (name)) + instance = getattr(mod, class_type)() + public_name = getattr(instance, 'name', name) + tree[public_name] = instance + cherrypy.log.error('Added module %s as %s' % (name, public_name)) def _load_classes(self, tree, path, class_type): files = None @@ -62,20 +66,73 @@ class Plugins(object): filename = os.path.join(path, name) self._load_class(tree, class_type, filename) - def get_providers(self): - if self._providers_tree is None: - path = None - if 'providers.dir' in cherrypy.config: - path = cherrypy.config['providers.dir'] - if not path: - path = os.path.join(self._path, 'providers') + def get_plugins(self, path, class_type): + plugins = dict() + self._load_classes(plugins, path, class_type) + return plugins - self._providers_tree = [] - self._load_classes(self._providers_tree, path, 'IdpProvider') - return self._providers_tree +class PluginLoader(object): - def get_custom(self, path, class_type): - tree = [] - self._load_classes(tree, path, class_type) - return tree + def __init__(self, baseobj, facility, plugin_type): + (whitelist, config) = Store().get_plugins_config(facility) + if cherrypy.config.get('debug', False): + cherrypy.log('[%s] %s: %s' % (facility, whitelist, config)) + if whitelist is None: + whitelist = [] + if config is None: + config = dict() + + p = Plugins(path=cherrypy.config['base.dir']) + (pathname, dummy) = os.path.split(inspect.getfile(baseobj)) + self._plugins = { + 'config': config, + 'available': p.get_plugins(pathname, plugin_type), + 'whitelist': whitelist, + 'enabled': [] + } + + def get_plugin_data(self): + return self._plugins + + +class PluginObject(object): + + def __init__(self): + self.name = None + self._config = None + self._options = None + + def get_config_desc(self): + """ 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 + """ + return self._options + + def set_config(self, config): + self._config = config + + 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] + + if cherrypy.config.get('debug', False): + cherrypy.log('[%s] %s: %s' % (self.name, name, value)) + + return value + + def set_config_value(self, option, value): + if not self._config: + self._config = dict() + self._config[option] = value -- 2.20.1