2 # Backupninja python reimplementation, based on original backupninja program
4 # Copyright (C) 2010 Matthijs Kooijman <matthijs@stdin.nl>
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License along
17 # with this program; if not, write to the Free Software Foundation, Inc.,
18 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 """ Action superclass with common functionality """
22 import sys, ConfigParser
25 from backupninja import config
27 def fail_on_exception(f):
29 This is a decorator meant for methods on the Action class. It
30 catches any exceptions thrown, sets the failed attribute to True and
31 rethrows the exception.
33 def inner(self, *args, **kwargs):
35 f(self, *args, **kwargs)
43 Subclasses of Action represent handlers for various action types.
44 This class is called Action instead of Handler, since even though the
45 classes could be referred to as handlers, the instances of this
46 class are really actions (i.e., it represents a specific action,
47 which is a combination of a action type and a specific action
51 # Subclasses should overwrite this with their default config
52 # See backupninja.config.load_config for the structure of this
54 self.default_config = {}
55 # Assume we'll run succesfully. If anything fails in the
56 # meanwhile, set this to True.
59 def run(self, **kwargs):
61 Run this action for a single target. Override this method
66 def finish(self, **kwargs):
68 Called when all targets have been processed. Can be overridden
73 def load_config(self, filename):
75 Load the configuration for this action from the given filename.
77 self.conf = config.load_config(filename, self.default_config)
79 def get_config_optional(self, section, option):
81 Returns the value of the given option. If the option was not set
82 (and no default was set in self.default_config), return None.
84 This is a convenience wrapper for ConfigParser.get(), since that
85 throws an exception on unset options.
88 return self.conf.get(section, option)
89 except ConfigParser.NoOptionError:
92 def get_config_mandatory(self, section, option):
94 Returns the value of the given option. If the option was not set
95 (and no default was set in self.default_config), raises a
96 backupninja.config.ConfigError.
98 This is a convenience wrapper for ConfigParser.get(), since that
99 has a very generic exception message on unknown options.
102 return self.conf.get(section, option)
103 except ConfigParser.NoOptionError:
104 raise config.ConfigError("Option '%s' in section '%s' is mandatory, please configure it" % (option, section))
106 def create_action(ty):
108 Create a new (subclass of) Action object for an action with the
111 If the handler class for this type cannot be loaded, an exception is
114 modname = 'backupninja.handlers.%s' % ty
115 # Load the handler if it is not loaded yet
116 if not modname in sys.modules:
117 log.debug('Loading handler for type "%s"', ty)
119 __import__(modname, globals(), locals(), [])
120 except ImportError, e:
121 # Add some extra info, since the default exception does not
122 # show the full module name.
123 raise ImportError('Cannot load module %s: %s' % (modname, e))
124 log.debug('Loaded handler for type "%s" from "%s"', ty, sys.modules[modname].__file__)
125 # Get the module from the module table
126 module = sys.modules[modname]
128 # Check that the module has a "handler" top level function, which
129 # should create a new Action object.
130 if not hasattr(module, 'handler'):
131 raise ImportError('%s is not valid: it '
132 'does not have a "handler" top level function.'
135 # Call the "handler" function to create the actual action
136 action = module.handler()
138 # Check if the handler returned is really a subclass of Action
139 if not isinstance(action, Action):
140 raise TypeError('%s is not valid, %s.handler did not return a '
141 'subclass of backupninja.handlers.Handler.'
142 % (module.__file__, modname))