pvlbackup-rsync-wrapper: add --snapshot-size / --snapshot-wait opts
authorTero Marttila <terom@paivola.fi>
Sun, 22 Apr 2012 12:31:26 +0300
changeset 42 43e27a3e9efe
parent 41 921b45c4678b
child 43 2911d4dd5a47
pvlbackup-rsync-wrapper: add --snapshot-size / --snapshot-wait opts
bin/pvlbackup-rsync-wrapper
pvl/backup/lvm.py
pvl/backup/rsync.py
--- 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