5f1b283094a8f27641ab327a622163650ef58e26
[matthijs/projects/backupninja.git] / src / lib / backupninja / config.py
1 # -*- mode: python; sh-basic-offset: 4; indent-tabs-mode: nil; -*-
2 # vim: set filetype=python sw=4 sts=4 expandtab autoindent:
3 #
4 #    Backupninja python reimplementation, based on original backupninja program
5 #    by riseup.net.
6 #    Copyright (C) 2010  Matthijs Kooijman <matthijs@stdin.nl>
7 #
8 #    This program is free software; you can redistribute it and/or modify
9 #    it under the terms of the GNU General Public License as published by
10 #    the Free Software Foundation; either version 2 of the License, or
11 #    (at your option) any later version.
12 #
13 #    This program is distributed in the hope that it will be useful,
14 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
15 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 #    GNU General Public License for more details.
17 #
18 #    You should have received a copy of the GNU General Public License along
19 #    with this program; if not, write to the Free Software Foundation, Inc.,
20 #    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21
22 """ Load configuration for backupninja and configured actions """
23
24 import os, ConfigParser
25
26 # Defaults for global configuration values
27 default_global_config = {}
28
29 import logging as log
30
31 class ConfigError(Exception):
32     """
33     An exception thrown when something is wrong with the config.
34     This is not thrown by the config module, but it is meant to be
35     thrown by handlers when they find something wrong with the
36     configuration contents.
37     """
38     pass
39
40 def get_global_config(opts):
41     """
42     Returns the global configuration, in a SafeConfigParser object.
43     If the configuration file can not be found, logs an error and
44     returns None.
45
46     opts are the parsed commandline options.
47     """
48     global_config = os.path.join(opts.config_dir, opts.global_config)
49     return load_config(global_config, default_global_config)
50
51 def list_actions(opts):
52     """
53     Lists all actions defined in the configuration directory. Returns a
54     list of full paths to action configuration files.
55
56     opts are the parsed commandline options.
57     """
58     actions_dir = os.path.join(opts.config_dir, opts.actions_dir)
59     return [os.path.join(actions_dir, f) 
60             for f in os.listdir(actions_dir) 
61             if not f.startswith('.')]
62     
63 def load_config(filename, defaults):
64     """
65     Load a configuration file, using the given default values.
66
67     The defaults argument contains a dictionary of sections. Each key is
68     a section name, each value is a dictionary of values (where the key
69     is the value name and the value is the actual value).
70     """
71     # Open a file and read it
72     config = ConfigParser.SafeConfigParser()
73     _set_default_config(config, defaults)
74     log.debug('Reading config file "%s"', filename)
75     try:
76         file = open(filename, 'r')
77     except IOError, e:
78         # Log the error and return None
79         msg = 'Unable to open configuration file "%s": %s' % (filename, e)
80         log.error(msg)
81         return None
82
83     config.readfp(file)
84     return config
85
86 def _set_default_config(parser, values):
87     """
88     Saves the values given to the ConfigParser given. This can be used
89     to store a set of default values to a ConfigParser before loading a
90     file (The defaults argument to the ConfigParser constructor only
91     sets the values of the "DEFAULT" section).
92
93     The values argument contains a dictionary of sections. Each key is a
94     section name, each value is a dictionary of values (where the key is
95     the value name and the value is the actual value).
96     """
97     for section, options in values.items():
98         if not parser.has_section(section):
99             parser.add_section(section)
100         for option, value in options.items():
101             # Interpret None as "no default", since ConfigParser doesn't
102             # like non-string values.
103             if not value is None:
104                 parser.set(section, option, value)