From: Matthijs Kooijman Date: Mon, 26 Jul 2010 17:04:03 +0000 (+0200) Subject: config: Restructure config loading to allow defaults. X-Git-Url: https://git.stderr.nl/gitweb?p=matthijs%2Fprojects%2Fbackupninja.git;a=commitdiff_plain;h=d64d30066c2cbcf02e1c4d05198ca430584b5cbd;ds=sidebyside config: Restructure config loading to allow defaults. Handlers now load their own configuration files, so they can pass default configuration values. This also allows for more complicated configuration loading in handlers by overriding the load_config method. --- diff --git a/src/lib/backupninja/action.py b/src/lib/backupninja/action.py index de36c4b..b4245f4 100644 --- a/src/lib/backupninja/action.py +++ b/src/lib/backupninja/action.py @@ -21,6 +21,7 @@ """ Running backup actions """ +import os import logging as log from backupninja import config @@ -35,39 +36,39 @@ def run_all_actions(opts, global_config): """ log.info('Running all actions') try: - actions = config.list_actions(opts) + action_configs = config.list_actions(opts) except OSError, e: log.critical('Unable to list actions: %s', e) return - actions.sort() + action_configs.sort() - for action in actions: - run_action(action, opts, global_config) + for action_config in action_configs: + run_action(action_config, opts, global_config) -def run_action(action, opts, global_config): +def run_action(action_config, opts, global_config): """ - Run a single action. opts are the parsed commandline options, + Run a single action. action_config is the full path to its + configuration file. opts are the parsed commandline options, global_config is the parsed global configuration. """ - log.info('Running action "%s"', action) # Split the action filename - parts = action.split('.') + parts = os.path.basename(action_config).split('.') if (len(parts) != 2): - log.error('Invalid action filename: "%s". Should be in the form name.type, where type is a valid handler.' % action) + log.error('Invalid action filename: "%s". Should be in the form name.type, where type is a valid handler.' % action_config) return (action_name, action_ty) = parts - - # Get the config for this action - action_config = config.get_action_config(opts, action) + log.info('Running action "%s.%s"', action_name, action_ty) try: # Create a handler for this action - handler = handlers.create_handler(action_ty, action_config) + handler = handlers.create_handler(action_ty) + # Let the handler load its configuration file + handler.load_config(action_config) # Run it handler.run(test=opts.test) handler.finish(test=opts.test) except Exception, e: - log.error('Running action "%s" failed: %s', action, e) + log.error('Running action "%s.%s" failed: %s', action_name, action_ty, e) import traceback log.debug(traceback.format_exc()) diff --git a/src/lib/backupninja/config.py b/src/lib/backupninja/config.py index 1e29290..0720f0a 100644 --- a/src/lib/backupninja/config.py +++ b/src/lib/backupninja/config.py @@ -23,6 +23,8 @@ import os, ConfigParser +# Defaults for global configuration values +default_global_config = {} import logging as log @@ -35,31 +37,31 @@ def get_global_config(opts): opts are the parsed commandline options. """ global_config = os.path.join(opts.config_dir, opts.global_config) - return _load_config(global_config) - -def get_action_config(opts, action): - """ - Returns the configuration for the named action, in a - SafeConfigParser object. If the configuration file can not be found, - logs an error and returns None. - - opts are the parsed commandline options. - """ - actions_dir = os.path.join(opts.config_dir, opts.actions_dir) - return _load_config(os.path.join(actions_dir, action)) + return load_config(global_config, default_global_config) def list_actions(opts): """ Lists all actions defined in the configuration directory. Returns a - list of action names that can be passed to get_action_config. + list of full paths to action configuration files. + opts are the parsed commandline options. """ actions_dir = os.path.join(opts.config_dir, opts.actions_dir) - return [f for f in os.listdir(actions_dir) if not f.startswith('.')] + return [os.path.join(actions_dir, f) + for f in os.listdir(actions_dir) + if not f.startswith('.')] -def _load_config(filename): +def load_config(filename, defaults): + """ + Load a configuration file, using the given default values. + + The defaults argument contains a dictionary of sections. Each key is + a section name, each value is a dictionary of values (where the key + is the value name and the value is the actual value). + """ # Open a file and read it config = ConfigParser.SafeConfigParser() + _set_default_config(config, defaults) log.debug('Reading config file "%s"', filename) try: file = open(filename, 'r') @@ -71,3 +73,20 @@ def _load_config(filename): config.readfp(file) return config + +def _set_default_config(parser, values): + """ + Saves the values given to the ConfigParser given. This can be used + to store a set of default values to a ConfigParser before loading a + file (The defaults argument to the ConfigParser constructor only + sets the values of the "DEFAULT" section). + + The values argument contains a dictionary of sections. Each key is a + section name, each value is a dictionary of values (where the key is + the value name and the value is the actual value). + """ + for section, options in values.items(): + if not parser.has_section(section): + parser.add_section(section) + for option, value in options.items(): + parser.set(section, option, value) diff --git a/src/lib/backupninja/handlers/__init__.py b/src/lib/backupninja/handlers/__init__.py index fb57bf4..ccb5c8c 100644 --- a/src/lib/backupninja/handlers/__init__.py +++ b/src/lib/backupninja/handlers/__init__.py @@ -22,9 +22,14 @@ import sys import logging as log +from backupninja import config + class Handler(object): - def __init__(self, conf): - self.conf = conf + def __init__(self): + # Subclasses should overwrite this with their default config + # See backupninja.config.load_config for the structure of this + # value. + self.default_config = {} def run(self, **kwargs): """ @@ -40,10 +45,18 @@ class Handler(object): """ pass -def create_handler(ty, conf): + def load_config(self, filename): + """ + Load the configuration for this action from the given filename. + """ + self.conf = config.load_config(filename, self.default_config) + + + +def create_handler(ty): """ Create a new (subclass of) Handler object for an action with the - given type. conf is the configuration to pass to the handler. + given type. If the handler cannot be loaded, an exception is thrown. """ @@ -69,7 +82,7 @@ def create_handler(ty, conf): % (module.__file__)) # Call the "handler" function to create the actual handler - handler = module.handler(conf) + handler = module.handler() # Check if the handler returned is really a subclass of Handler if not isinstance(handler, Handler):