config: Restructure config loading to allow defaults.
authorMatthijs Kooijman <matthijs@stdin.nl>
Mon, 26 Jul 2010 17:04:03 +0000 (19:04 +0200)
committerMatthijs Kooijman <matthijs@stdin.nl>
Mon, 26 Jul 2010 17:17:59 +0000 (19:17 +0200)
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.

src/lib/backupninja/action.py
src/lib/backupninja/config.py
src/lib/backupninja/handlers/__init__.py

index de36c4b..b4245f4 100644 (file)
@@ -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())
index 1e29290..0720f0a 100644 (file)
@@ -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)
index fb57bf4..ccb5c8c 100644 (file)
 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):