# HG changeset patch # User Tero Marttila # Date 1335087086 -10800 # Node ID 43e27a3e9efe87dbd9c09e482ead6a5dffb93350 # Parent 921b45c4678b649729ed92a0ec8d48760323fef2 pvlbackup-rsync-wrapper: add --snapshot-size / --snapshot-wait opts diff -r 921b45c4678b -r 43e27a3e9efe bin/pvlbackup-rsync-wrapper --- a/bin/pvlbackup-rsync-wrapper Sun Apr 22 12:13:26 2012 +0300 +++ b/bin/pvlbackup-rsync-wrapper Sun Apr 22 12:31:26 2012 +0300 @@ -4,13 +4,15 @@ SSH authorized_keys command="..." wrapper for rsync. Testing goes something like: - sudo sh -c "PYTHONPATH=. rsync -e './scripts/pvlbackup-rsync-wrapper --debug -C --' -ax testing:lvm:asdf:test test/tmp" + sudo PYTHONPATH=. ./bin/pvlbackup-rsync-wrapper --command 'rsync --server --sender -ax . lvm:asdf:test' -vD + + sudo sh -c "PYTHONPATH=. rsync -e './bin/pvlbackup-rsync-wrapper --debug -C --' -ax testing:lvm:asdf:test test/tmp" """ from pvl.backup import __version__ from pvl.backup.rsync import RSyncCommandFormatError from pvl.backup.invoke import InvokeError -from pvl.backup import rsync +from pvl.backup import rsync, lvm import optparse import shlex @@ -60,6 +62,13 @@ parser.add_option('-P', '--restrict-path', metavar='PATH', default=False, help="restrict to given path prefix") + # lvm options + parser.add_option('-L', '--snapshot-size', metavar='SIZE', default=lvm.LVM_SNAPSHOT_SIZE, + help="create snapshot with given LV size (used to store writes during backup)") + + parser.add_option('--snapshot-wait', metavar='SECONDS', default=lvm.LVM_SNAPSHOT_WAIT, type='float', + help="wait for snapshot to settle after unmounting") + # defaults parser.set_defaults( debug_for = [], @@ -106,6 +115,7 @@ # parse the source path as given by the client, may be a real path or pseudo-path source = rsync.parse_source(path, restrict_path = options.restrict_path, + lvm_opts = dict(size = options.snapshot_size, wait = options.snapshot_wait), ) except RSyncCommandFormatError, e: @@ -135,7 +145,7 @@ host = args.pop(0) command_parts = args - log.debug("using command from args: %r", command_parts) + log.debug("host=%r, using command from args: %r", host, command_parts) # args elif args : diff -r 921b45c4678b -r 43e27a3e9efe pvl/backup/lvm.py --- a/pvl/backup/lvm.py Sun Apr 22 12:13:26 2012 +0300 +++ b/pvl/backup/lvm.py Sun Apr 22 12:31:26 2012 +0300 @@ -7,9 +7,16 @@ import contextlib import os.path import logging +import time log = logging.getLogger('pvl.backup.lvm') +# default snapshot size +LVM_SNAPSHOT_SIZE = '5G' + +# number of seconds to wait for lvm snapshot to settle after unmount.. +LVM_SNAPSHOT_WAIT = 5 + class LVMError (Exception) : pass @@ -60,30 +67,33 @@ return LVMVolume(self, name) @contextlib.contextmanager - def snapshot (self, base, **kwargs) : + def snapshot (self, base, wait=LVM_SNAPSHOT_WAIT, **opts) : """ A Context Manager for handling an LVMSnapshot. See LVMSnapshot.create() with lvm.snapshot(lv) as snapshot : ... + + wait - wait given interval for the snapshot device to settle before unmounting it + **opts - LVMSnapshot.create() options (e.g. size) """ - log.debug("creating snapshot from {base}: {opts}".format(base=base, opts=kwargs)) - snapshot = LVMSnapshot.create(self, base, **kwargs) + log.debug("creating snapshot from {base}: wait={wait} {opts}".format(base=base, wait=wait, opts=opts)) + snapshot = LVMSnapshot.create(self, base, **opts) try : log.debug("got: {0}".format(snapshot)) yield snapshot finally: - # XXX: there's some timing bug with an umount leaving the LV open, do we need to wait for it to get closed after mount? - # https://bugzilla.redhat.com/show_bug.cgi?id=577798 - # some udev event bug, possibly fixed in lvm2 2.02.86? - # try to just patiently wait for it to settle down... if this isn't enough, we need some dmremove magic - log.debug("cleanup: waiting for snapshot volume to settle...") - import time - time.sleep(1) + if wait : + # XXX: there's some timing bug with an umount leaving the LV open, do we need to wait for it to get closed after mount? + # https://bugzilla.redhat.com/show_bug.cgi?id=577798 + # some udev event bug, possibly fixed in lvm2 2.02.86? + # try to just patiently wait for it to settle down... if this isn't enough, we need some dmremove magic + log.debug("cleanup: waiting %.2f seconds for snapshot volume to settle...", wait) + time.sleep(wait) # cleanup log.debug("cleanup: {0}".format(snapshot)) @@ -166,9 +176,6 @@ """ LVM snapshot """ - - # default snapshot size - LVM_SNAPSHOT_SIZE = '5G' # base lv base = None @@ -185,7 +192,7 @@ name = '{name}-{tag}'.format(name=base.name, tag=tag) # snapshot instance - snapshot = cls(lvm, base, name) + snapshot = cls(lvm, base, name, size=size) ## verify # base should exist diff -r 921b45c4678b -r 43e27a3e9efe pvl/backup/rsync.py --- a/pvl/backup/rsync.py Sun Apr 22 12:13:26 2012 +0300 +++ b/pvl/backup/rsync.py Sun Apr 22 12:31:26 2012 +0300 @@ -62,14 +62,22 @@ Backup LVM LV by snapshotting + mounting it. """ - def __init__ (self, volume) : + def __init__ (self, volume, **opts) : + """ + volume - the LVMVolume to snapshot + **opts - options for LVM.snapshot + """ + RSyncServer.__init__(self) self.volume = volume + self.snapshot_opts = opts def execute (self, options) : """ Snapshot, mount, execute + + options - list of rsync options """ # backup target from LVM command @@ -80,7 +88,7 @@ log.info("Open snapshot: %s", volume) # XXX: generate snapshot nametag to be unique? - with lvm.snapshot(volume, tag='backup') as snapshot: + with lvm.snapshot(volume, tag='backup', **self.snapshot_opts) as snapshot: # mount log.info("Mounting snapshot: %s", snapshot) @@ -160,9 +168,11 @@ return cmd, options, source, dest -def parse_source (path, restrict_path=False) : +def parse_source (path, restrict_path=False, lvm_opts={}) : """ Figure out source to rsync from, based on pseudo-path given in rsync command. + + lvm_opts - dict of **opts for RSyncLVMServer """ # normalize @@ -196,7 +206,7 @@ lvm = LVM(vg) volume = lvm.volume(lv) - return RSyncLVMServer(volume) + return RSyncLVMServer(volume, **lvm_opts) else : # invalid