Changeset 28edb3adc96a3b765795c2ed0be9da72e031acbf

Show
Ignore:
Timestamp:
03/07/07 21:58:57 (8 years ago)
Author:
Theo Schlossnagle <jesus@omniti.com>
git-committer:
Theo Schlossnagle <jesus@omniti.com> 1173304737 +0000
git-parent:

[cb639856e3261b2963a940623c42f8a8481eb078]

git-author:
Theo Schlossnagle <jesus@omniti.com> 1173304737 +0000
Message:

first pass at lock handling, refs #6

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • zetaback

    rcb63985 r28edb3a  
    55use MIME::Base64; 
    66use POSIX qw/strftime/; 
     7use Fcntl qw/:flock/; 
    78use Pod::Usage; 
    89 
    9 use vars qw/%conf $version_string 
     10use vars qw/%conf %locks $version_string 
    1011            $CONF $BLOCKSIZE $DEBUG $HOST $BACKUP 
    1112            $RESTORE $RESTORE_HOST $RESTORE_ZFS $TIMESTAMP 
     
    357358  return "$bytes b"; 
    358359} 
     360sub lock($;$$) { 
     361  my ($host, $file, $nowait) = @_; 
     362  print "Acquiring lock for $host:$file\n" if($DEBUG); 
     363  $file ||= 'master.lock'; 
     364  my $store = config_get($host, 'store'); 
     365  $store =~ s/%h/$host/g; 
     366  return 1 if(exists($locks{"$host:$file"})); 
     367  open(LOCK, "+>>$store/$file") || return 0; 
     368  unless(flock(LOCK, LOCK_EX | ($nowait ? LOCK_NB : 0))) { 
     369    close(LOCK); 
     370    return 0; 
     371  } 
     372  $locks{"$host:$file"} = \*LOCK; 
     373  return 1; 
     374} 
     375sub unlock($;$$) { 
     376  my ($host, $file, $remove) = @_; 
     377  print "Releasing lock for $host:$file\n" if($DEBUG); 
     378  $file ||= 'master.lock'; 
     379  my $store = config_get($host, 'store'); 
     380  $store =~ s/%h/$host/g; 
     381  return 0 unless(exists($locks{"$host:$file"})); 
     382  *UNLOCK = $locks{$file}; 
     383  unlink("$store/$file") if($remove); 
     384  flock(UNLOCK, LOCK_UN); 
     385  close(UNLOCK); 
     386  return 1; 
     387} 
    359388sub scan_for_backups($) { 
    360389  my %info = (); 
     
    719748  print "Planning '$host'\n" if($DEBUG); 
    720749  my $agent = config_get($host, 'agent'); 
    721   open(ZFSLIST, "ssh $host $agent -l |") || next; 
    722   foreach my $diskline (<ZFSLIST>) { 
    723     chomp($diskline); 
    724     next unless($diskline =~ /^(\S+) \[([^\]]*)\]/); 
    725     my $diskname = $1; 
    726     my %snaps; 
    727     map { $snaps{$_} = 1 } (split(/,/, $2)); 
    728  
    729     # If we are being selective (via -z) now is the time. 
    730     next 
    731       if($diskpat &&          # if the pattern was specified it could 
    732          !($diskname eq $diskpat ||        # be a specific match or a 
    733            ($diskpat =~ /^\/(.+)\/$/ && $diskname =~ /$1/))); # regex 
    734  
    735     print " => Scanning '$store' for old backups of '$diskname'.\n" if($DEBUG); 
    736     # Make directory on demand 
    737     my $backup_info = scan_for_backups($store); 
    738     # That gave us info on all backups, we just want this disk 
    739     $backup_info = $backup_info->{$diskname} || {}; 
    740  
    741     # Should we do a backup? 
    742     my $backup_type = 'no'; 
    743     if(time() > $backup_info->{last_backup} + config_get($host, 'backup_interval')) { 
    744       $backup_type = 'incremental'; 
    745     } 
    746     if(time() > $backup_info->{last_full} + config_get($host, 'full_interval')) { 
    747       $backup_type = 'full'; 
    748     } 
    749  
    750     # If we want an incremental, but have no full, then we need to upgrade to full 
    751     if($backup_type eq 'incremental') { 
    752       my $have_full_locally = 0; 
    753       # For each local full backup, see if the full backup still exists on the other end. 
    754       foreach (keys %{$backup_info->{'full'}}) { 
    755         $have_full_locally = 1 if(exists($snaps{'__zb_full_' . $_})); 
    756       } 
    757       $backup_type = 'full' unless($have_full_locally); 
    758     } 
    759  
    760     print " => doing $backup_type backup\n" if($DEBUG); 
    761     # We need to drop a __zb_base snap or a __zb_incr snap before we proceed 
    762     unless($NUETERED) { 
    763       if($backup_type eq 'full') { 
    764         eval { zfs_full_backup($host, $diskname, $store); }; 
    765         if ($@) { 
    766           chomp(my $err = $@); 
    767           print " => failure $err\n"; 
     750  my $took_action = 1; 
     751  while($took_action) { 
     752    $took_action = 0; 
     753    my @disklist; 
     754 
     755    # We need a lock for the listing. 
     756    return unless(lock($host, ".list")); 
     757    open(ZFSLIST, "ssh $host $agent -l |") || next; 
     758    @disklist = grep { chomp } (<ZFSLIST>); 
     759    close(ZFSLIST); 
     760 
     761    foreach my $diskline (@disklist) { 
     762      chomp($diskline); 
     763      next unless($diskline =~ /^(\S+) \[([^\]]*)\]/); 
     764      my $diskname = $1; 
     765      my %snaps; 
     766      map { $snaps{$_} = 1 } (split(/,/, $2)); 
     767   
     768      # If we are being selective (via -z) now is the time. 
     769      next 
     770        if($diskpat &&          # if the pattern was specified it could 
     771           !($diskname eq $diskpat ||        # be a specific match or a 
     772             ($diskpat =~ /^\/(.+)\/$/ && $diskname =~ /$1/))); # regex 
     773   
     774      print " => Scanning '$store' for old backups of '$diskname'.\n" if($DEBUG); 
     775 
     776      # Make directory on demand 
     777      my $backup_info = scan_for_backups($store); 
     778      # That gave us info on all backups, we just want this disk 
     779      $backup_info = $backup_info->{$diskname} || {}; 
     780   
     781      # Should we do a backup? 
     782      my $backup_type = 'no'; 
     783      if(time() > $backup_info->{last_backup} + config_get($host, 'backup_interval')) { 
     784        $backup_type = 'incremental'; 
     785      } 
     786      if(time() > $backup_info->{last_full} + config_get($host, 'full_interval')) { 
     787        $backup_type = 'full'; 
     788      } 
     789   
     790      # If we want an incremental, but have no full, then we need to upgrade to full 
     791      if($backup_type eq 'incremental') { 
     792        my $have_full_locally = 0; 
     793        # For each local full backup, see if the full backup still exists on the other end. 
     794        foreach (keys %{$backup_info->{'full'}}) { 
     795          $have_full_locally = 1 if(exists($snaps{'__zb_full_' . $_})); 
    768796        } 
    769         else { 
    770           # Unless there was an error backing up, remove all the other full snaps 
    771           foreach (keys %snaps) { 
    772             zfs_remove_snap($host, $diskname, $_) if(/^__zb_full_(\d+)/)  
     797        $backup_type = 'full' unless($have_full_locally); 
     798      } 
     799   
     800      print " => doing $backup_type backup\n" if($DEBUG); 
     801      # We need to drop a __zb_base snap or a __zb_incr snap before we proceed 
     802      unless($NUETERED || $backup_type eq 'no') { 
     803        # attempt to lock this action, if it fails, skip -- someone else is working it. 
     804        next unless(lock($host, dir_encode($diskname), 1)); 
     805        unlock($host, '.list'); 
     806 
     807        if($backup_type eq 'full') { 
     808          eval { zfs_full_backup($host, $diskname, $store); }; 
     809          if ($@) { 
     810            chomp(my $err = $@); 
     811            print " => failure $err\n"; 
    773812          } 
     813          else { 
     814            # Unless there was an error backing up, remove all the other full snaps 
     815            foreach (keys %snaps) { 
     816              zfs_remove_snap($host, $diskname, $_) if(/^__zb_full_(\d+)/)  
     817            } 
     818          } 
     819          $took_action = 1; 
    774820        } 
    775       } 
    776       if($backup_type eq 'incremental') { 
    777         zfs_remove_snap($host, $diskname, '__zb_incr') if($snaps{'__zb_incr'}); 
    778         # Find the newest full from which to do an incremental (NOTE: reverse numeric sort) 
    779         my @fulls = sort { $b <=> $a } (keys %{$backup_info->{'full'}}); 
    780         zfs_incremental_backup($host, $diskname, $fulls[0], $store); 
    781       } 
    782     } 
    783   } 
    784   close(ZFSLIST); 
     821        if($backup_type eq 'incremental') { 
     822          zfs_remove_snap($host, $diskname, '__zb_incr') if($snaps{'__zb_incr'}); 
     823          # Find the newest full from which to do an incremental (NOTE: reverse numeric sort) 
     824          my @fulls = sort { $b <=> $a } (keys %{$backup_info->{'full'}}); 
     825          zfs_incremental_backup($host, $diskname, $fulls[0], $store); 
     826          $took_action = 1; 
     827        } 
     828        unlock($host, dir_encode($diskname), 1); 
     829      } 
     830      last if($took_action); 
     831    } 
     832    unlock($host, '.list'); 
     833  } 
    785834} 
    786835