--- 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 :
--- 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
--- 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