Bacula-users

Re: [Bacula-users] Backing up lvm snapshots?

2011-09-18 14:04:07
Subject: Re: [Bacula-users] Backing up lvm snapshots?
From: Kelvin Raywood <kray AT triumf DOT ca>
To: bacula-users AT lists.sourceforge DOT net
Date: Sun, 18 Sep 2011 10:35:44 -0700 (PDT)
I've interspersed some comments.  I've also appended a python script 
that I use for taking shanpshots of _mounted_ logical volumes, and 
mounting the snapshot.  The same script is used for unmounting and 
releasing the snapshot.

This is not a solution for your problem because your LV is not a 
file-system, but contains partitions with file-systems.  Nevertheless, 
you may be able to use it as a starting point and make appropriate 
modifications for your case.

Hope this helps - Kel Raywood

On Sun, 18 Sep 2011, Tobias Schenk wrote:
> I try use bacula 5.0.3 on suse linux to backup lvm snapshots.
> Basically, I create a lvm snapshot called /dev/vmguests/backup using the
> RunScript directive. Unfortunately, this is a sym link to /dev/dm-6. I
> cannot be sure it is always dm-6. I would like to follow the sym link
> and backup /dev/dm-6 as raw device.

There is a command "readlink" which you can use to find the target of a 
symbolic link.

e.g.  tgt=$( readlink --canonicalize /dev/vmguests/backup )

> The hints I found while googling are are not working (like appending /.
> to the File directive) or obscure (like using bash scripting to resolve
> File names).

Hmm ... I then I guess you would classify using readlink in a script as 
obscure.

> I cannot simply mount /dev/dm-6 to somewhere because the contents is a
> partitioned raw device of a kvm instance.

Then you need to use kpartx to create device maps from the partition 
tables.  These devices would contain the file-systems which you could 
mount.

> Maybe it is possible resolve the link using a bash script in the file
> directive but I have not found information about when the File directive
> will be evaluated relative to RunScript.

A RunScript with "RunsWhen = Before" will do exactly that. ie. nothing 
else will be evaluated until after the script has returned.

> Has anyone a working solution for lvm snapshots?

As mentioned at the top, I have a client-side script for backing up 
mounted LVs.  ie.  You don't pass it the LV name, you pass it the 
mount-point (e.g. /home) and where you want to mount the snapshot (e.g. 
/.snap/home) .  In the FileSet I use "strip path = 1"

My job resources contain:

     RunScript {
         Runs When = Before
         Runs On Client = yes
         Abort Job On Error = yes
         Command = "/etc/bacula/scripts/snapshot -L 4G /home /.snap/home"
     }
     RunScript {
         Runs When = After
         Runs On Client = yes
         Runs On Failure = yes
         Command = "/etc/bacula/scripts/snapshot -d /.snap/home"
     }

Client-seide python-script "/etc/bacula/scripts/snapshot":

#!/bin/env python

import sys

def usage():
     prg = sys.argv[0]
     print "Usage: %s [-L size] FileSystemPath SnapshotPath" % prg
     print "___or: %s -d SnapshotPath" % prg
     sys.exit(2)


class snapshotError(Exception):
     pass

from commands import getstatusoutput

def do_or_die( cmd ):
     stat,msg = getstatusoutput( cmd )
     if stat:
         raise snapshotError( msg )
     return msg

from os import path

GET_LV_ATTR = '/usr/sbin/lvs --noheadings -o lv_attr %s'
CREATE_SS   = '/usr/sbin/lvcreate --size %s --snapshot --name %s %s'
REMOVE_LV   = '/usr/sbin/lvremove -f %s'
LOAD_SS_MOD = '/sbin/modprobe dm-snapshot'

class Volume(object):
     """A mounted logical-volume"""

     def __init__( self, dir ):
         """ Read /proc/mounts to determine the device and file-system type.
             Read /etc/mtab to get the mount options.
         """
         self.dev = None
         for line in open( '/proc/mounts' ).readlines():
             tokens = line.split()[:3]

             if tokens[1] == dir:
                 self.attr = do_or_die( GET_LV_ATTR % tokens[0] ).strip()
                 self.dev, self.mnt, self.fs = tokens
                 break

         if not self.dev:
             raise snapshotError( '"%s" not a mount point' % dir)

         self.opts = None
         for line in open( '/etc/mtab' ).readlines():
             tokens = line.split()[:4]
             if tokens[1] == dir:
                 self.opts = tokens[3]
                 break

     def take_snapshot( self, size, ss_mnt ):

         # Require that this is a primary logical-volume
         if self.attr[0] not in '-o':
             raise snapshotError('"%s" not a primary logical-volume'
                                % self.dev
                                )

         # Ensure that the snapshot kernel-module is loaded
         do_or_die( LOAD_SS_MOD )

         # Change option "rw" to "ro"
         if self.opts:
             opts = ','.join( [ s == 'rw' and 'ro' or s
                                for s in self.opts.split(',')
                              ]
                            )
         else:
             opts = 'ro'

         # Add options for xfs
         if self.fs == 'xfs':
             opts = ','.join( [ opts, 'nouuid' ] )

         # Create the snapshot and its mount point, then mount it.
         # Use the prefix SS_ for the name of the snapshot
         head, tail = path.split( self.dev )
         ss_name = 'SS_' + tail
         ss_dev  = path.join( head, ss_name )
         print do_or_die( CREATE_SS % (size, ss_name, self.dev) )

         # Release the snapshot if mkdir or mount fail
         try:
             print do_or_die( '/bin/mkdir -v -p %s' % ss_mnt )
             cmd = '/bin/mount -v -r -t %s -o %s %s %s' \
                      % (self.fs, opts, ss_dev, ss_mnt)
             print cmd
             print do_or_die( cmd )
         except:
             print do_or_die( REMOVE_LV % ss_dev )
             raise

     def release_snapshot( self ):

         # Require that this is a snapshot logical-volume"""
         if self.attr[0] not in 'sS':
             raise snapshotError( '"%s" not a snapshot logical-volume' )

         do_or_die( '/bin/umount %s' % self.mnt )
         do_or_die( REMOVE_LV % self.dev )
         self.dev = None
         do_or_die( 'rmdir --ignore-fail-on-non-empty %s' % self.mnt )
         self.mnt = None

from getopt import getopt, GetoptError

def main():
     try:
         opts, args = getopt( sys.argv[1:], 'L:d' )
     except GetoptError:
         usage()

     nargs = len( args )
     delete = False
     size = '2G'    # Default size for COW space

     for opt, parm in opts:
         if opt == "-L":
             size = parm
         elif opt == "-d":
             delete = True

     try:
         if delete:
             if nargs != 1 :
                 usage()
             Volume( args[0] ).release_snapshot()
         else:
             if nargs != 2:
                 usage()
             Volume( args[0] ).take_snapshot( size, args[1] )
     except snapshotError, msg:
         print "ERROR:", msg
         sys.exit(1)

if __name__ == "__main__":
     main()

------------------------------------------------------------------------------
BlackBerry&reg; DevCon Americas, Oct. 18-20, San Francisco, CA
http://p.sf.net/sfu/rim-devcon-copy2
_______________________________________________
Bacula-users mailing list
Bacula-users AT lists.sourceforge DOT net
https://lists.sourceforge.net/lists/listinfo/bacula-users