From: <fcr...@us...> - 2012-04-14 11:17:18
|
Revision: 820 http://safekeep.svn.sourceforge.net/safekeep/?rev=820&view=rev Author: fcrawford Date: 2012-04-14 11:17:12 +0000 (Sat, 14 Apr 2012) Log Message: ----------- Major rework of snapshot mounting Modified Paths: -------------- safekeep/trunk/safekeep Modified: safekeep/trunk/safekeep =================================================================== --- safekeep/trunk/safekeep 2012-04-06 13:22:43 UTC (rev 819) +++ safekeep/trunk/safekeep 2012-04-14 11:17:12 UTC (rev 820) @@ -68,7 +68,10 @@ base_dir = None current_pid = os.getpid() default_bandwidth = {} -default_mountoptions = { 'xfs' : 'nouuid' } +# Default mount options, overridden elsewhere: +# Key is a file system type, or 'snapshot' for default for snapshot mount +# or 'bind' for a bind mount (check mount for details) +default_mountoptions = { 'xfs' : 'ro,nouuid', 'snapshot' : 'ro', 'bind' : 'ro' } PROTOCOL = "1.2" VERSION = "1.4.0" @@ -644,9 +647,11 @@ mounts.append(matches.groups()) return mounts +def normalise_lvm_device(device): + return device.replace('/mapper','').replace('-','/').replace('//', '-') + def map_lvm_device(device): - device = device.replace('/mapper','').replace('-','/').replace('//', '-') - return device.split('/')[-2:] + return normalise_lvm_device(device).split('/')[-2:] def check_lvm_information(device): (group, volume) = map_lvm_device(device) @@ -655,13 +660,6 @@ return True return False -def gather_lvm_information(device): - (group, volume) = map_lvm_device(device) - for (device, mountpoint, mounttype, mountoptions) in mount_information(False): - if [group, volume] == map_lvm_device(device): - return (group, volume, mountpoint, mounttype) - return (None, None, None, None) - def do_lvremove(device): (group, volume) = device.split('/')[-2:] if group == 'mapper': @@ -681,9 +679,9 @@ ret = 0 # Equivalent to lvremove succeeding return ret -def gather_snap_information(device, bdir): - (group, volume, mountpoint, mounttype) = gather_lvm_information(device) - if not mountpoint: return (None, None, None, None) +def generate_snap_information(device, bdir, mountpoint, mounttype): + assert os.path.ismount(mountpoint) + (group, volume) = map_lvm_device(device) lvmdev = os.path.join('/dev', group, volume) if bdir[-1] == '/': bdir = bdir[:-1] snapname = '%s_snap_%s' % (volume, os.path.basename(bdir)) @@ -691,58 +689,77 @@ if os.path.isabs(mountpoint[0]): mountpoint = mountpoint[1:] return (lvmdev, snapdev, os.path.join(bdir, mountpoint), mounttype) -def do_client_snap(cfg, bdir): +def do_client_snap_device(snap, bdir, mountpoint, mounttype): assert is_temp_root(bdir) - debug('Doing FS snapshots') - for snap in cfg['snaps']: - device = snap['device'] - (lvmdev, snapdev, snapmnt, snaptyp) = gather_snap_information(device, bdir) - if not snapmnt: - warn('Cannot find the mountpoint for: %s' % device) - continue - args = ['lvcreate'] - if snap['tags']: - for tag in snap['tags']: - args.extend(['--addtag', tag.strip()]) - args.extend(['--snapshot', '--size', snap['size'], - '--name', os.path.basename(snapdev), lvmdev]) - ec = spawn(args) - if ec: - warn('Can not snapshot the device: %s' % device) - continue - # no need to mkdir since the mountpoint already exists - args = ['mount', '-t', snaptyp] - if snap['mountoptions']: - args.extend(['-o', snap['mountoptions']]) - elif snaptyp in default_mountoptions: - args.extend(['-o', default_mountoptions[snaptyp]]) - args.extend([snapdev, snapmnt]) - ec = spawn(args) - if ec: - warn('Can not mount the snapshot: %s' % device) - ret = do_lvremove(snapdev) - if ret: - warn('Can not tear down snapshot: %s' % device) + device = snap['device'] + debug('Doing FS snapshot for %s' % device) + (lvmdev, snapdev, snapmnt, snaptyp) = generate_snap_information(device, bdir, mountpoint, mounttype) + if not os.path.isdir(snapmnt): + warn('Cannot find the mountpoint %s for %s' % (snapmnt, device)) + return False + if 'snapdevice' in snap: + warn('bind mount of snapshot not currently supported: %s' % mountpoint) + return True # Should be False, but this will still do a good backup + args = ['lvcreate'] + if snap['tags']: + for tag in snap['tags']: + args.extend(['--addtag', tag.strip()]) + args.extend(['--snapshot', '--size', snap['size'], + '--name', os.path.basename(snapdev), lvmdev]) + ec = spawn(args) + if ec: + warn('Can not snapshot the device: %s' % device) + return False + # no need to mkdir since the mountpoint already exists + args = ['mount', '-t', snaptyp] + if snap['mountoptions']: + args.extend(['-o', snap['mountoptions']]) + elif snaptyp in default_mountoptions: + args.extend(['-o', default_mountoptions[snaptyp]]) + elif 'snapshot' in default_mountoptions: + args.extend(['-o', default_mountoptions['snapshot']]) + args.extend([snapdev, snapmnt]) + ec = spawn(args) + if ec: + warn('Can not mount the snapshot: %s' % device) + ret = do_lvremove(snapdev) + if ret: + warn('Can not tear down snapshot: %s' % device) + return False + snap['snapdevice'] = snapdev + snap['mountpoint'] = snapmnt + return True + def do_client_snap_teardown(cfg, bdir): assert is_temp_root(bdir) debug('Tear down FS snapshots dumps') snaps = list(cfg['snaps']) snaps.reverse() for snap in snaps: + if 'mountpoint' in snap: del snap['mountpoint'] device = snap['device'] - (lvmdev, snapdev, snapmnt, snaptyp) = gather_snap_information(device, bdir) - if not snapmnt: - warn('Can not find the mountpoint for: %s' % device) + if not 'snapdevice' in snap: + warn('No snapdevice for device teardown: %s' % device) continue - ret = spawn(['umount', snapmnt]) - if ret: - warn('Can not umount the snapshot: %s' % snapmnt) + snapdev = snap['snapdevice'] + debug('Tear down FS snapshot dump for %s -> %s' % (snapdev, device)) + ret = do_lvremove(snapdev) if ret: warn('Can not tear down snapshot: %s' % device) + continue + del snap['snapdevice'] +def find_snapshot(cfg, device): + for snap in cfg['snaps']: + if normalise_lvm_device(snap['device']) == normalise_lvm_device(device): + debug('find_snapshot device matched: %s' % device) + return snap + debug('find_snapshot device no matched for %s' % device) + return None + def mount_excluded(cfg, mountpoint): debug("mount_excluded: %s" % mountpoint) if not mountpoint.endswith('/'): mountpoint = mountpoint + '/' @@ -754,19 +771,42 @@ debug("mount_excluded: %s: no matches" % mountpoint) return False +def do_umount_all(bdir): + assert is_temp_root(bdir) + total_ret = 0 + for (device, mountpoint, mounttype, mountoptions) in mount_information(True): + if mountpoint.startswith(bdir): + debug("Removing mount %s" % mountpoint) + ret = spawn(['umount', mountpoint]) + if ret: + warn('Can not unmount snapshot: %s' % mountpoint) + total_ret += ret + return total_ret + def do_rbind(cfg, startpath, bdir): for (device, mountpoint, mounttype, mountoptions) in mount_information(False): debug("Testing %s on %s" % (mountpoint, device)) if mountpoint.startswith(startpath) and device.startswith('/'): if not mount_excluded(cfg, mountpoint): - ret = spawn(['mount', '--bind', mountpoint, reroot(bdir, mountpoint)]) + snap = find_snapshot(cfg, device) + if snap: + ret = not do_client_snap_device(snap, bdir, mountpoint, mounttype) + else: + ret = spawn(['mount', '--bind', mountpoint, reroot(bdir, mountpoint)]) + if ret: + debug("mount --bind %s: failed: unwinding" % mountpoint) + else: + spawn(['mount', '--make-unbindable', reroot(bdir, mountpoint)]) + if 'bind' in default_mountoptions: + spawn(['mount' , '-o', + ('remount,%s,bind' % default_mountoptions['bind']), + mountpoint, reroot(bdir, mountpoint)]) if ret: - debug("mount --bind %s: failed: unwinding" % mountpoint) - ret = spawn(['umount', '-l', reroot(bdir, startpath)]) + ret = spawn(['umount', reroot(bdir, startpath)]) if ret: warn('Failed to unmount: %s' % reroot(bdir, startpath)) + do_client_snap_teardown(cfg, bdir) return 1 - spawn(['mount', '--make-unbindable', reroot(bdir, mountpoint)]) return 0 @@ -822,7 +862,7 @@ for snap in cfg['snaps']: device = snap['device'] if check_lvm_information(device) and not do_client_scrub(): - raise Exception("Previous snapshots found for %s and automatic correction failed: run 'safekeep --server --cleanup' to correct" % device) + raise Exception("Previous snapshots found for %s and automatic correction failed: run 'safekeep --server --cleanup %s' to correct" % (device, cfg['host'])) ret = spawn(['modprobe', 'dm-snapshot']) if ret: @@ -836,8 +876,6 @@ except OSError, e: warn('Failed to remove: %s: %s' % (bdir, e)) bdir = '/' - else: - do_client_snap(cfg, bdir) else: bdir = '/' debug('Working root is %s' % bdir) @@ -847,17 +885,17 @@ def do_client_cleanup(cfg, bdir): debug('Do cleanup of %s in %s' % (cfg['host'], bdir)) if is_temp_root(bdir): - do_client_snap_teardown(cfg, bdir) - - ret = spawn(['umount', '-l', bdir]) + ret = do_umount_all(bdir) if ret: - warn('Failed to unmount: %s' % bdir) + warn('Failed to unmount tree: %s' % bdir) else: try: os.rmdir(bdir) except OSError, e: warn('Unable to remove: %s: %s' % (bdir, e)) + do_client_snap_teardown(cfg, bdir) + do_client_dbdump_teardown(cfg) def do_client_compat(server_versions): @@ -903,9 +941,9 @@ info("Removing mount %s" % mountpoint) if device == '/' and 'bind' in mountoptions.split(','): info("Removing rbind directory %s" % mountpoint) - ret = spawn(['umount', '-l', mountpoint]) + ret = do_umount_all(mountpoint) if ret: - warn('Failed to unmount: %s' % mountpoint) + warn('Failed to unmount tree: %s' % mountpoint) else: try: os.rmdir(mountpoint) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |