pvl/backup/rsync.py
changeset 43 2911d4dd5a47
parent 42 43e27a3e9efe
child 44 7069af6b7025
equal deleted inserted replaced
42:43e27a3e9efe 43:2911d4dd5a47
    34 class RSyncServer (object) :
    34 class RSyncServer (object) :
    35     """
    35     """
    36         rsync server-mode execution.
    36         rsync server-mode execution.
    37     """
    37     """
    38 
    38 
    39     def _execute (self, options, path) :
    39     def _execute (self, options, srcdst, path) :
    40         """
    40         """
    41             Underlying rsync just reads from filesystem.
    41             Underlying rsync just reads from filesystem.
    42         """
    42 
       
    43                 options     - list of rsync options
       
    44                 srcdst      - the (source, dest) pair with None placeholder, as returned by parse_command
       
    45                 path        - the real path to replace None with
       
    46         """
       
    47     
       
    48         # one of this will be None
       
    49         src, dst = srcdst
       
    50 
       
    51         # replace None -> path
       
    52         src = src or path
       
    53         dst = dst or path
       
    54 
       
    55         log.debug("%r -> %r", src, dst)
    43         
    56         
    44         # invoke directly, no option-handling, nor stdin/out redirection
    57         # invoke directly, no option-handling, nor stdin/out redirection
    45         invoke.invoke(RSYNC, options + ['.', path], data=False)
    58         invoke.invoke(RSYNC, options + [ src, dst ], data=False)
    46 
    59 
    47 class RSyncFSServer (RSyncServer) :
    60 class RSyncFSServer (RSyncServer) :
    48     """
    61     """
    49         Normal filesystem backup.
    62         Normal filesystem backup.
    50     """
    63     """
    52     def __init__ (self, path) :
    65     def __init__ (self, path) :
    53         RSyncServer.__init__(self)
    66         RSyncServer.__init__(self)
    54 
    67 
    55         self.path = path
    68         self.path = path
    56 
    69 
    57     def execute (self, options) :
    70     def execute (self, options, srcdst) :
    58         return self._execute(options, self.path)
    71         """
       
    72                 options     - list of rsync options
       
    73                 srcdst      - the (source, dest) pair with None placeholder, as returned by parse_command
       
    74         """
       
    75 
       
    76         return self._execute(options, srcdst, self.path)
    59 
    77 
    60 class RSyncLVMServer (RSyncServer) :
    78 class RSyncLVMServer (RSyncServer) :
    61     """
    79     """
    62         Backup LVM LV by snapshotting + mounting it.
    80         Backup LVM LV by snapshotting + mounting it.
    63     """
    81     """
    71         RSyncServer.__init__(self)
    89         RSyncServer.__init__(self)
    72 
    90 
    73         self.volume = volume
    91         self.volume = volume
    74         self.snapshot_opts = opts
    92         self.snapshot_opts = opts
    75  
    93  
    76     def execute (self, options) :
    94     def execute (self, options, srcdst) :
    77         """
    95         """
    78             Snapshot, mount, execute
    96             Snapshot, mount, execute
    79 
    97 
    80                 options     - list of rsync options
    98                 options     - list of rsync options
       
    99                 srcdst      - the (source, dest) pair with None placeholder, as returned by parse_command
    81         """
   100         """
    82         
   101         
    83         # backup target from LVM command
   102         # backup target from LVM command
    84         lvm = self.volume.lvm
   103         lvm = self.volume.lvm
    85         volume = self.volume
   104         volume = self.volume
    95             with mount(snapshot.dev_path, name_hint=('lvm_' + snapshot.name + '_'), readonly=True) as mountpoint:
   114             with mount(snapshot.dev_path, name_hint=('lvm_' + snapshot.name + '_'), readonly=True) as mountpoint:
    96                 # rsync!
   115                 # rsync!
    97                 log.info("Running rsync: %s", mountpoint)
   116                 log.info("Running rsync: %s", mountpoint)
    98 
   117 
    99                 # with trailing slash
   118                 # with trailing slash
   100                 return self._execute(options, mountpoint.path + '/')
   119                 return self._execute(options, srcdst, mountpoint.path + '/')
   101 
   120 
   102             # cleanup
   121             # cleanup
   103         # cleanup
   122         # cleanup
   104  
   123  
   105 def parse_command (command_parts, restrict_server=True, restrict_readonly=True) :
   124 def parse_command (command_parts, restrict_server=True, restrict_readonly=True) :
   108 
   127 
   109             command_parts       - the command-list sent by rsync
   128             command_parts       - the command-list sent by rsync
   110             restrict_server     - restrict to server-mode
   129             restrict_server     - restrict to server-mode
   111             restrict_readonly   - restrict to read/send-mode
   130             restrict_readonly   - restrict to read/send-mode
   112         
   131         
       
   132         In server mode, source will always be '.', and dest the source/dest.
       
   133         
   113         Returns:
   134         Returns:
   114 
   135 
   115             (cmd, options, source, dest)
   136             (cmd, options, path, (source, dest))
       
   137 
       
   138             path            -> the real source path
       
   139             (source, dest)  -> combination of None for path, and the real source/dest
       
   140 
   116     """
   141     """
   117 
   142 
   118     cmd = None
   143     cmd = None
   119     options = []
   144     options = []
   120     source = None
   145     source = None
   137     # options
   162     # options
   138     have_server = ('--server' in options)
   163     have_server = ('--server' in options)
   139     have_sender = ('--sender' in options)
   164     have_sender = ('--sender' in options)
   140 
   165 
   141     # verify
   166     # verify
   142     if not have_server :
   167     if restrict_server and not have_server :
   143         raise RSyncCommandFormatError("Missing --server")
   168         raise RSyncCommandFormatError("Missing --server")
   144 
   169 
   145     if restrict_readonly and not have_sender :
   170     if restrict_readonly and not have_sender :
   146         raise RSyncCommandFormatError("Missing --sender for readonly")
   171         raise RSyncCommandFormatError("Missing --sender for readonly")
   147 
   172 
   148     # parse path
   173     if not source :
       
   174         raise RSyncCommandFormatError("Missing source path")
       
   175         
       
   176     if not dest:
       
   177         raise RSyncCommandFormatError("Missing dest path")
       
   178 
       
   179 
       
   180     # parse real source
   149     if have_sender :
   181     if have_sender :
   150         # read
   182         # read; first arg will always be .
   151         # XXX: which way does the dot go?
       
   152         if source != '.' :
   183         if source != '.' :
   153             raise RSyncCommandFormatError("Invalid dest for sender")
   184             raise RSyncCommandFormatError("Invalid dest for sender")
   154         
   185 
   155         path = dest
   186         path = dest
   156 
   187         dest = None
   157     else :
   188         
       
   189         log.debug("using server/sender source path: %s", path)
       
   190 
       
   191     elif have_server :
   158         # write
   192         # write
   159         if source != '.' :
   193         if source != '.' :
   160             raise RSyncCommandFormatError("Invalid source for reciever")
   194             raise RSyncCommandFormatError("Invalid source for reciever")
   161 
   195 
   162         path = dest
   196         path = dest
   163 
   197         dest = None
   164     if not path :
   198         
   165         raise RSyncCommandFormatError("Missing path")
   199         log.debug("using server dest path: %s", path)
       
   200 
       
   201     else :
       
   202         # local src -> dst
       
   203         path = source
       
   204         source = None
       
   205 
       
   206         log.debug("using local src path: %s -> %s", path, dest)
   166 
   207 
   167     # ok
   208     # ok
   168     return cmd, options, source, dest
   209     return cmd, options, path, (source, dest)
   169 
       
   170       
   210       
   171 def parse_source (path, restrict_path=False, lvm_opts={}) :
   211 def parse_source (path, restrict_path=False, lvm_opts={}) :
   172     """
   212     """
   173         Figure out source to rsync from, based on pseudo-path given in rsync command.
   213         Figure out source to rsync from, based on pseudo-path given in rsync command.
   174 
   214 
   196             lvm, vg, lv = path.split(':')
   236             lvm, vg, lv = path.split(':')
   197 
   237 
   198         except ValueError, e:
   238         except ValueError, e:
   199             raise RSyncCommandFormatError("Invalid lvm pseudo-path: {error}".format(error=e))
   239             raise RSyncCommandFormatError("Invalid lvm pseudo-path: {error}".format(error=e))
   200         
   240         
   201         # XXX: validate
   241         # XXX: validate?
   202 
       
   203         log.info("LVM: %s/%s", vg, lv)
   242         log.info("LVM: %s/%s", vg, lv)
   204 
   243 
   205         # open
   244         # open
   206         lvm = LVM(vg)
   245         lvm = LVM(vg)
   207         volume = lvm.volume(lv)
   246         volume = lvm.volume(lv)