scripts/pvlbackup-rsync-snapshot
changeset 17 b88653920e7a
parent 16 d4b9954273a1
child 21 de69e9ba8f22
equal deleted inserted replaced
16:d4b9954273a1 17:b88653920e7a
    28         Parse command-line arguments.
    28         Parse command-line arguments.
    29     """
    29     """
    30 
    30 
    31     parser = optparse.OptionParser(
    31     parser = optparse.OptionParser(
    32             prog        = argv[0],
    32             prog        = argv[0],
    33             usage       = '%prog: [options] --source <src> --destination <dst>',
    33             usage       = '%prog: [options] [ --config <path> | --target <path> [ --source <src> ] [ --interval <name> ] ]',
    34 
    34 
    35             # module docstring
    35             # module docstring
    36             # XXX: breaks multi-line descriptions..
    36             # XXX: breaks multi-line descriptions..
    37             description = __doc__,
    37             description = __doc__,
    38     )
    38     )
    72 
    72 
    73     #
    73     #
    74     parser.add_option('-c', '--config',     metavar='FILE',
    74     parser.add_option('-c', '--config',     metavar='FILE',
    75         help="Load configuration file")
    75         help="Load configuration file")
    76 
    76 
    77 
       
    78     #
    77     #
    79     parser.add_option('-s', '--source',     metavar='RSYNC-PATH',
    78     parser.add_option('-T', '--target',    metavar='PATH',
    80         help="Backup source in rsync-syntax")
    79         help="Target path")
    81 
    80 
    82     parser.add_option('-d', '--destination',    metavar='RSYNC-PATH',
    81     parser.add_option('-s', '--source',     metavar='RSYNC-PATH', dest='target_source', default=False,
    83         help="Backup destination in rsync-syntax")
    82         help="Run target backup from source in rsync-syntax")
    84 
    83 
    85     parser.add_option('--interval',         metavar='NAME', action='append', dest='target_intervals',
    84     parser.add_option('--interval',         metavar='NAME', action='append', dest='target_intervals',
    86         help="Enable given interval")
    85         help="Run target with given given interval(s)")
    87 
    86 
    88 
    87 
    89     # defaults
    88     # defaults
    90     parser.set_defaults(
    89     parser.set_defaults(
    91         loglevel    = logging.WARNING,
    90         loglevel            = logging.WARNING,
       
    91 
       
    92         target_intervals    = [],
    92     )
    93     )
    93     parser.set_defaults(**defaults)
    94     parser.set_defaults(**defaults)
    94 
    95 
    95     
    96     
    96     # parse
    97     # parse
   255             )
   256             )
   256         else :
   257         else :
   257             # partial instance with keep
   258             # partial instance with keep
   258             return cls(name,
   259             return cls(name,
   259                 format  = base.format,
   260                 format  = base.format,
   260                 keep    = config_int('keep', arg),
   261                 keep    = config_int('keep', arg) if arg else base.keep,
   261             )
   262             )
   262 
   263 
   263     def __init__ (self, name, format, keep) :
   264     def __init__ (self, name, format, keep) :
   264         self.name = name
   265         self.name = name
   265         self.format = format
   266         self.format = format
   284 
   285 
   285         # subsections
   286         # subsections
   286         intervals       = None,
   287         intervals       = None,
   287         rsync_options   = None,
   288         rsync_options   = None,
   288     ) :
   289     ) :
   289         if not source :
   290         if not source and source is not False :
   290             raise ConfigError("Missing required option: source for [target:{name}]".format(name=name))
   291             raise ConfigError("Missing required option: source for [target:{name}]".format(name=name))
   291 
   292 
   292         # global defaults
   293         # global defaults
   293         _rsync_options = dict(options.rsync_options)
   294         _rsync_options = dict(options.rsync_options)
   294 
   295 
   336         self.snapshots_dir = os.path.join(self.path, 'snapshots')
   337         self.snapshots_dir = os.path.join(self.path, 'snapshots')
   337 
   338 
   338         # 'current' symlink
   339         # 'current' symlink
   339         self.current_path = os.path.join(self.path, 'current')
   340         self.current_path = os.path.join(self.path, 'current')
   340 
   341 
   341     def snapshot (self, options, now) :
   342     def prepare (self, options) :
   342         """
   343         """
   343             Perform the rsync from our source to self.snapshot_dir.
   344             Prepare dir for usage
   344 
   345         """
   345             XXX: allocate snapshot_name here?
   346 
   346         """
   347         if not os.path.exists(self.path) :
       
   348             raise Exception("Missing target dir: {path}".format(path=self.path))
   347 
   349 
   348         if not os.path.exists(self.snapshots_dir) :
   350         if not os.path.exists(self.snapshots_dir) :
   349             log.warn("Creating snapshots dir: %s", self.snapshots_dir)
   351             log.warn("Creating snapshots dir: %s", self.snapshots_dir)
   350             os.mkdir(self.snapshots_dir)
   352             os.mkdir(self.snapshots_dir)
   351         
   353 
       
   354     def snapshot (self, options, now) :
       
   355         """
       
   356             Perform the rsync from our source to self.snapshot_dir.
       
   357 
       
   358             XXX: allocate snapshot_name here?
       
   359         """
       
   360        
   352         # new snapshot
   361         # new snapshot
   353         snapshot_name = now.strftime(options.snapshot_format)
   362         snapshot_name = now.strftime(options.snapshot_format)
   354         snapshot_path = os.path.join(self.snapshots_dir, snapshot_name)
   363         snapshot_path = os.path.join(self.snapshots_dir, snapshot_name)
   355         temp_path = os.path.join(self.snapshots_dir, 'tmp')
   364         temp_path = os.path.join(self.snapshots_dir, 'tmp')
   356 
   365 
   572     def run (self, options) :
   581     def run (self, options) :
   573         """
   582         """
   574             Execute
   583             Execute
   575         """
   584         """
   576 
   585 
       
   586         # prep
       
   587         self.prepare(options)
       
   588 
   577         # clean intervals?
   589         # clean intervals?
   578         if options.clean_intervals:
   590         if options.clean_intervals:
   579             for interval in self.intervals :
   591             for interval in self.intervals :
   580                 log.info("Cleaning interval: %s...", interval)
   592                 log.info("Cleaning interval: %s...", interval)
   581 
   593 
   604         return 1
   616         return 1
   605 
   617 
   606     def __str__ (self) :
   618     def __str__ (self) :
   607         return self.name
   619         return self.name
   608 
   620 
   609 def run (options) :
   621 def run (options, targets) :
   610     # default config
   622     # default config
   611     config = dict(
   623     config = dict(
   612         rsync_options   = {},
   624         rsync_options   = {},
   613         intervals       = {},
   625         intervals       = {},
   614         targets         = {},
   626         targets         = {},
   617     if options.config :
   629     if options.config :
   618         # load
   630         # load
   619         config = parse_config(options.config, config)
   631         config = parse_config(options.config, config)
   620  
   632  
   621     # manual?
   633     # manual?
   622     if options.destination :
   634     if options.target :
   623         config['targets']['<commandline>'] = dict(
   635         config['targets'][options.target] = dict(
   624             path        = options.destination,
   636             path        = options.target,
   625             source      = options.source,
   637             source      = options.target_source,
   626             intervals   = options.target_intervals,
   638             intervals   = dict((name, None) for name in options.target_intervals),
   627         )
   639         )
   628   
   640   
   629     # intervals
   641     # intervals
   630     for name in config['intervals'] :
   642     for name in config['intervals'] :
   631         interval_config = config['intervals'][name]
   643         interval_config = config['intervals'][name]
   647         log.debug("rsync option: %s=%s", option, value)
   659         log.debug("rsync option: %s=%s", option, value)
   648 
   660 
   649         # store
   661         # store
   650         options.rsync_options[option] = value
   662         options.rsync_options[option] = value
   651 
   663 
   652     for name in config['targets'] :
   664     # all targets?
       
   665     if not targets :
       
   666         targets = list(config['targets'])
       
   667 
       
   668     # targets
       
   669     for name in targets :
   653         target_config = config['targets'][name]
   670         target_config = config['targets'][name]
   654 
   671 
   655         # parse
   672         # parse
   656         target = Target.from_config(options, name, **target_config)
   673         target = Target.from_config(options, name, **target_config)
   657 
   674 
   710     defaults = config_defaults()
   727     defaults = config_defaults()
   711 
   728 
   712     # global options + args
   729     # global options + args
   713     options, args = parse_options(argv, defaults)
   730     options, args = parse_options(argv, defaults)
   714 
   731 
   715     # XXX: args?
   732     # args: filter targets
   716     if args :
   733     # XXX: fix name mangling
   717         log.error("No arguments are handled")
   734     targets = [target.replace('-', '_') for target in args]
   718         return 2
       
   719 
   735 
   720     try :
   736     try :
   721         # handle it
   737         # handle it
   722         return run(options)
   738         return run(options, targets)
   723 
   739 
   724     except Exception, e:
   740     except Exception, e:
   725         log.error("Internal error:", exc_info=e)
   741         log.error("Internal error:", exc_info=e)
   726         return 3
   742         return 3
   727 
   743