scripts/pvlbackup-rsync-snapshot
changeset 14 2a7b87dc6c45
parent 12 fbfdde7326f4
child 15 61f6d0ca0432
equal deleted inserted replaced
13:bbda233b91c8 14:2a7b87dc6c45
    43     general.add_option('-q', '--quiet',      dest='loglevel', action='store_const', const=logging.WARNING, help="Less output")
    43     general.add_option('-q', '--quiet',      dest='loglevel', action='store_const', const=logging.WARNING, help="Less output")
    44     general.add_option('-v', '--verbose',    dest='loglevel', action='store_const', const=logging.INFO,  help="More output")
    44     general.add_option('-v', '--verbose',    dest='loglevel', action='store_const', const=logging.INFO,  help="More output")
    45     general.add_option('-D', '--debug',      dest='loglevel', action='store_const', const=logging.DEBUG, help="Even more output")
    45     general.add_option('-D', '--debug',      dest='loglevel', action='store_const', const=logging.DEBUG, help="Even more output")
    46 
    46 
    47     parser.add_option_group(general)
    47     parser.add_option_group(general)
       
    48 
       
    49     # rsync
       
    50     rsync = optparse.OptionGroup(parser, "rsync Options")
       
    51 
       
    52     rsync.add_option('--exclude-from',       metavar='FILE',
       
    53         help="Read exclude rules from given file")
       
    54 
       
    55     rsync.add_option('--include-from',       metavar='FILE',
       
    56         help="Read include rules from given file")
       
    57 
       
    58     parser.add_option_group(rsync)
    48 
    59 
    49     #
    60     #
    50     parser.add_option('-s', '--source',     metavar='RSYNC-PATH',
    61     parser.add_option('-s', '--source',     metavar='RSYNC-PATH',
    51         help="Backup source in rsync-syntax")
    62         help="Backup source in rsync-syntax")
    52 
    63 
    84             'delete':           True,
    95             'delete':           True,
    85         },
    96         },
    86 
    97 
    87         # datetime formats for intervals
    98         # datetime formats for intervals
    88         interval_format = {
    99         interval_format = {
    89             'all':      None,       # default to snapshot_format
   100             'recent':   None,       # default to snapshot_format
    90             'day':      '%Y-%m-%d',
   101             'day':      '%Y-%m-%d',
    91             'week':     '%Y-%W',
   102             'week':     '%Y-%W',
    92             'month':    '%Y-%m',
   103             'month':    '%Y-%m',
    93             'year':     '%Y',
   104             'year':     '%Y',
    94         },
   105         },
    95 
   106 
    96         # retention for intervals
   107         # retention for intervals
    97         interval_retention = {
   108         interval_retention = {
    98             'all':      4,
   109             'recent':   4,
    99             'day':      7,
   110             'day':      7,
   100             'week':     4,
   111             'week':     4,
   101             'month':    4,
   112             'month':    4,
   102             'year':     1,
   113             'year':     1,
   103         },
   114         },
   119         level   = options.loglevel,
   130         level   = options.loglevel,
   120     )
   131     )
   121 
   132 
   122     if options.clean :
   133     if options.clean :
   123         options.clean_intervals = options.clean_snapshots = options.clean
   134         options.clean_intervals = options.clean_snapshots = options.clean
       
   135 
       
   136     if options.include_from :
       
   137         options.rsync_options['include-from'] = options.include_from
       
   138 
       
   139     if options.exclude_from :
       
   140         options.rsync_options['exclude-from'] = options.exclude_from
   124 
   141 
   125     return options, args
   142     return options, args
   126 
   143 
   127 def run_snapshot (options) :
   144 def run_snapshot (options) :
   128     """
   145     """
   147 
   164 
   148     # build rsync options
   165     # build rsync options
   149     opts = dict(options.rsync_options)
   166     opts = dict(options.rsync_options)
   150 
   167 
   151     if os.path.exists(options.current_path) :
   168     if os.path.exists(options.current_path) :
   152         # use as link-dest base; hardlinks unchanged files
   169         # real path to target
   153         opts['link-dest'] = options.current_path
   170         target = os.readlink(options.current_path)
       
   171         target_path = os.path.join(os.path.dirname(options.current_path), target)
       
   172         target_abs = os.path.abspath(target_path)
       
   173 
       
   174         log.info("Using current -> %s as base", target_path)
       
   175 
       
   176         # use as link-dest base; hardlinks unchanged files; target directory must be empty
       
   177         # rsync links absolute paths..
       
   178         opts['link-dest'] = target_abs
   154 
   179 
   155     # go
   180     # go
   156     log.debug("rsync %s -> %s", options.source, temp_path)
   181     log.debug("rsync %s -> %s", options.source, temp_path)
   157     rsync.rsync(options.source, temp_path, **opts)
   182     rsync.rsync(options.source, temp_path, **opts)
   158 
   183 
   191 
   216 
   192     # already there?
   217     # already there?
   193     if os.path.exists(path) :
   218     if os.path.exists(path) :
   194         target = os.readlink(path)
   219         target = os.readlink(path)
   195 
   220 
   196         log.info("Found existing %s: %s -> %s", interval, name, target)
   221         log.info("%s: Found existing: %s -> %s", interval, name, target)
   197 
   222 
   198     else :
   223     else :
   199         # update
   224         # update
   200         target = os.path.join('..', 'snapshots', snapshot_name)
   225         target = os.path.join('..', 'snapshots', snapshot_name)
   201 
   226 
   202         log.info("Updating %s: %s -> %s", interval, name, target)
   227         log.info("%s: Updating: %s -> %s", interval, name, target)
   203         log.debug("%s -> %s", path, target)
   228         log.debug("%s -> %s", path, target)
   204 
   229 
   205         os.symlink(target, path)
   230         os.symlink(target, path)
   206 
   231 
   207 
   232 
   347     """
   372     """
   348 
   373 
   349     # timestamp for run
   374     # timestamp for run
   350     options.now = datetime.datetime.now()
   375     options.now = datetime.datetime.now()
   351 
   376 
       
   377     # clean intervals?
       
   378     if options.clean_intervals:
       
   379         for interval in options.intervals :
       
   380             log.info("Cleaning interval: %s...", interval)
       
   381 
       
   382             clean_interval(options, interval)
       
   383 
       
   384     # clean snapshots?
       
   385     if options.clean_snapshots :
       
   386         log.info("Cleaning snapshots...")
       
   387 
       
   388         clean_snapshots(options)
       
   389 
   352     # snapshot from source?
   390     # snapshot from source?
   353     if options.source :
   391     if options.source :
   354         # base snapshot (symlink)
   392         # base snapshot (symlink)
   355         options.current_path = os.path.join(options.destination, 'current')
   393         options.current_path = os.path.join(options.destination, 'current')
   356 
   394 
   372         if not options.intervals :
   410         if not options.intervals :
   373             log.info("No --intervals given; not running any")
   411             log.info("No --intervals given; not running any")
   374 
   412 
   375         else :
   413         else :
   376             # maintain intervals
   414             # maintain intervals
   377             log.info("Running intervals: %s", options.intervals)
   415             log.info("Updating %d intervals...", len(options.intervals))
   378 
   416 
   379             for interval in options.intervals :
   417             for interval in options.intervals :
   380                 log.debug("%s", interval)
   418                 log.debug("%s", interval)
   381 
   419 
       
   420                 log.info("Updating interval: %s", interval)
       
   421 
   382                 # update
   422                 # update
   383                 update_interval(options, snapshot_name, interval)
   423                 update_interval(options, snapshot_name, interval)
   384 
       
   385     # clean intervals?
       
   386     if options.clean_intervals:
       
   387         for interval in options.intervals :
       
   388             log.info("Cleaning interval: %s...", interval)
       
   389 
       
   390             clean_interval(options, interval)
       
   391 
       
   392     # clean snapshots?
       
   393     if options.clean_snapshots :
       
   394         log.info("Cleaning snapshots...")
       
   395 
       
   396         clean_snapshots(options)
       
   397 
   424 
   398     # ok
   425     # ok
   399     return 1
   426     return 1
   400 
   427 
   401 def main (argv) :
   428 def main (argv) :