Subversion Repositories zfs_utils

Rev

Rev 43 | Rev 46 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 43 Rev 44
Line 497... Line 497...
497
 
497
 
498
   return 1;
498
   return 1;
499
}
499
}
500
 
500
 
501
# make a bunch of replicate commands and return them to the caller as a list
501
# make a bunch of replicate commands and return them to the caller as a list
-
 
502
# $sourceSnapsRef - list of snapshots on source machine
-
 
503
# $targetSnapsRef - list of snapshots on target machine
502
# $rootDataSet - string, the root of the snapshots exclusive of the dataset itself
504
# $dataset - The name of the dataset we are working on (same on both source and target)
-
 
505
# $sourceParent - The parent dataset of $dataset on source
-
 
506
# $targetParent - The parent dataset of $dataset on target
503
# $sourceSnapsRef
507
# $newStatusRef - A place to put the updated $targetSnapsRef
504
# $statusRef
508
# returns hashref of commands to execute, of form
505
# $newStatusRef
509
#    {$dataset} = "zfs send command"
506
# returns
510
# where $dataset above can be a child of $dataset
507
sub makeReplicateCommands {
511
sub makeReplicateCommands {
508
   my ( $sourceSnapsRef, $statusRef, $newStatusRef) = @_;
512
   my ( $sourceSnapsRef, $targetSnapsRef, $dataset, $sourceParent, $targetParent, $newStatusRef ) = @_;
509
   $sourceSnapsRef ||= [];
513
   $sourceSnapsRef ||= [];
510
   $statusRef     ||= [];
514
   $targetSnapsRef     ||= [];
511
   $newStatusRef  ||= [];
515
   $newStatusRef  ||= [];
-
 
516
   $sourceParent //= '';
-
 
517
   $sourceParent .= '/' unless $sourceParent eq '' or substr($sourceParent, -1) eq '/';
-
 
518
   $targetParent //= '';
-
 
519
   $targetParent .= '/' unless $targetParent eq '' or substr($targetParent, -1) eq '/';
-
 
520
 
-
 
521
   my %commands; # this will hold the commands (and the dataset as key) for return
-
 
522
 
-
 
523
   fatalError( "No dataset defined in makeReplicateCommands, can not continue") unless $dataset;
-
 
524
 
-
 
525
   # filter only the target and source snapshots which have this dataset in them, then remove
-
 
526
   # the parent of each.
-
 
527
   my $targetSnaps = [ map{ s/^$targetParent//r } grep{ /$dataset/ } @$targetSnapsRef ];
-
 
528
   my $sourceSnaps = [ map{ s/^$sourceParent//r } grep{ /$dataset/ } @$sourceSnapsRef ];
-
 
529
 
-
 
530
   #print "Dataset => [$dataset]\nSource Parent => [$sourceParent]\nTarget Parent => [$targetParent]\n";
-
 
531
   #print "Source Snaps\n" . Dumper( $sourceSnapsRef) . "\nTarget Snaps\n" . Dumper( $targetSnapsRef) . "\n";
-
 
532
 
-
 
533
   #print Dumper( $targetSnaps ) . "\n" . Dumper( $sourceSnaps ) . "\n"; die;
-
 
534
   #return \%commands;
512
 
535
 
513
   # parse snapshots: each line is expected to have snapshot fullname as first token: pool/fs@snap ...
536
   # parse snapshots: each line is expected to have snapshot fullname as first token: pool/fs@snap ...
514
   my %snaps_by_fs;
537
   my %snaps_by_fs;
515
   foreach my $line (@$sourceSnapsRef) {
538
   foreach my $line (@$sourceSnaps) {
516
      next unless defined $line && $line =~ /\S/;
539
      next unless defined $line && $line =~ /\S/;
517
      my ($tok) = split /\s+/, $line;
540
      my ($tok) = split /\s+/, $line;
518
      next unless $tok && $tok =~ /@/;
541
      next unless $tok && $tok =~ /@/;
519
      my ($fs, $snap) = split /@/, $tok, 2;
542
      my ($fs, $snap) = split /@/, $tok, 2;
520
      push @{ $snaps_by_fs{$fs} }, $snap;
543
      push @{ $snaps_by_fs{$fs} }, $snap;
Line 522... Line 545...
522
 
545
 
523
   # nothing to do
546
   # nothing to do
524
   return [] unless keys %snaps_by_fs;
547
   return [] unless keys %snaps_by_fs;
525
 
548
 
526
   # figure root filesystem: first snapshot line's fs is the requested root
549
   # figure root filesystem: first snapshot line's fs is the requested root
527
   my ($first_line) = grep { defined $_ && $_ =~ /\S/ } @$sourceSnapsRef;
550
   my ($first_line) = grep { defined $_ && $_ =~ /\S/ } @$sourceSnaps;
528
   my ($root_fs) = $first_line ? (split(/\s+/, $first_line))[0] =~ /@/ ? (split(/@/, (split(/\s+/, $first_line))[0]))[0] : undef : undef;
551
   my ($root_fs) = $first_line ? (split(/\s+/, $first_line))[0] =~ /@/ ? (split(/@/, (split(/\s+/, $first_line))[0]))[0] : undef : undef;
529
   $root_fs ||= (sort keys %snaps_by_fs)[0];
552
   $root_fs ||= (sort keys %snaps_by_fs)[0];
530
 
553
 
531
   # helper: find last status entry for a filesystem (status lines contain full snapshot names pool/fs@snap)
554
   # helper: find last status entry for a filesystem (status lines contain full snapshot names pool/fs@snap)
532
   my %last_status_for;
555
   my %last_status_for;
533
   for my $s (@$statusRef) {
556
   for my $s (@$targetSnaps) {
534
      next unless $s && $s =~ /@/;
557
      next unless $s && $s =~ /@/;
535
      my ($fs, $snap) = split /@/, $s, 2;
558
      my ($fs, $snap) = split /@/, $s, 2;
536
      $last_status_for{$fs} = $snap;    # later entries override earlier ones -> last occurrence kept
559
      $last_status_for{$fs} = $snap;    # later entries override earlier ones -> last occurrence kept
537
   }
560
   }
538
 
561
 
Line 549... Line 572...
549
   # decide if we can do a single recursive send:
572
   # decide if we can do a single recursive send:
550
   # condition: all 'to' snapshot names are identical
573
   # condition: all 'to' snapshot names are identical
551
   my %to_names = map { $_ => 1 } values %to_for;
574
   my %to_names = map { $_ => 1 } values %to_for;
552
   my $single_to_name = (keys %to_names == 1) ? (keys %to_names)[0] : undef;
575
   my $single_to_name = (keys %to_names == 1) ? (keys %to_names)[0] : undef;
553
 
576
 
554
   my %commands;
-
 
555
 
-
 
556
   if ($single_to_name) {
577
   if ($single_to_name) {
557
      # check whether any from is missing
578
      # check whether any from is missing
558
      my @from_values = map { $from_for{$_} } sort keys %from_for;
579
      my @from_values = map { $from_for{$_} } sort keys %from_for;
559
      my $any_from_missing = grep { !defined $_ } @from_values;
580
      my $any_from_missing = grep { !defined $_ } @from_values;
560
      my %from_names = map { $_ => 1 } grep { defined $_ } @from_values;
581
      my %from_names = map { $_ => 1 } grep { defined $_ } @from_values;
561
      my $single_from_name = (keys %from_names == 1) ? (keys %from_names)[0] : undef;
582
      my $single_from_name = (keys %from_names == 1) ? (keys %from_names)[0] : undef;
562
 
583
 
563
      if ($any_from_missing) {
584
      if ($any_from_missing) {
564
         # full recursive send from root
585
         # full recursive send from root
565
         $commands{$root_fs} = sprintf('zfs send -R %s@%s', $root_fs, $single_to_name);
586
         $commands{$root_fs} = sprintf('zfs send -R %s%s@%s', $sourceParent, $root_fs, $single_to_name);
566
      }
587
      }
567
      elsif ($single_from_name) {
588
      elsif ($single_from_name) {
568
         # incremental recursive send, but don't do it if they are the same
589
         # incremental recursive send, but don't do it if they are the same
569
         $commands{$root_fs} = sprintf('zfs send -R -I %s@%s %s@%s',
590
         $commands{$root_fs} = sprintf('zfs send -R -I %s%s@%s %s%s@%s',
570
                           $root_fs, $single_from_name, $root_fs, $single_to_name)
591
                           $sourceParent, $root_fs, $single_from_name, $sourceParent, $root_fs, $single_to_name)
571
                           unless $single_from_name eq $single_to_name;
592
                           unless $single_from_name eq $single_to_name;
572
      }
593
      }
573
      else {
594
      else {
574
         # from snapshots differ across children -> fall back to per-filesystem sends
595
         # from snapshots differ across children -> fall back to per-filesystem sends
575
         foreach my $fs (sort keys %to_for) {
596
         foreach my $fs (sort keys %to_for) {
576
            my $to  = $to_for{$fs};
597
            my $to  = $to_for{$fs};
577
            my $from = $from_for{$fs};
598
            my $from = $from_for{$fs};
578
            if ($from) {
599
            if ($from) {
579
               # if from and to are different, add it
600
               # if from and to are different, add it
580
               $commands{$fs} = sprintf('zfs send -I %s@%s %s@%s', $fs, $from, $fs, $to)
601
               $commands{$fs} = sprintf('zfs send -I %s%s@%s %s%s@%s', $sourceParent, $fs, $from, $sourceParent, $fs, $to)
581
                  unless $from eq $to;
602
                  unless $from eq $to;
582
            } else {
603
            } else {
583
               $commands{$fs} = sprintf('zfs send %s@%s', $fs, $to);
604
               $commands{$fs} = sprintf('zfs send %s%s@%s', $sourceParent, $fs, $to);
584
            }
605
            }
585
         }
606
         }
586
      }
607
      }
587
 
608
 
588
      # update new status: record newest snap for every filesystem
609
      # update new status: record newest snap for every filesystem
589
      foreach my $fs (keys %to_for) {
610
      foreach my $fs (keys %to_for) {
590
         push @$newStatusRef, sprintf('%s@%s', $fs, $to_for{$fs});
611
         push @$newStatusRef, sprintf('%s%s@%s', $targetParent, $fs, $to_for{$fs});
591
      }
612
      }
592
   } else {
613
   } else {
593
      # not all children share same newest snap -> per-filesystem sends
614
      # not all children share same newest snap -> per-filesystem sends
594
      foreach my $fs (sort keys %to_for) {
615
      foreach my $fs (sort keys %to_for) {
595
         my $to  = $to_for{$fs};
616
         my $to  = $to_for{$fs};
596
         my $from = $from_for{$fs};
617
         my $from = $from_for{$fs};
597
         if ($from) {
618
         if ($from) {
598
            $commands{$fs} = sprintf('zfs send -I %s@%s %s@%s', $fs, $from, $fs, $to);
619
            $commands{$fs} = sprintf('zfs send -I %s%s@%s %s%s@%s', $sourceParent, $fs, $from, $sourceParent, $fs, $to);
599
         } else {
620
         } else {
600
            $commands{$fs} = sprintf('zfs send %s@%s', $fs, $to);
621
            $commands{$fs} = sprintf('zfs send %s%s@%s', $sourceParent, $fs, $to);
601
         }
622
         }
602
         push @$newStatusRef, sprintf('%s@%s', $fs, $to);
623
         push @$newStatusRef, sprintf('%s%s@%s', $targetParent, $fs, $to);
603
      }
624
      }
604
   }
625
   }
605
 
626
 
606
   # return arrayref of commands (caller can iterate or join with pipes)
627
   # return arrayref of commands (caller can iterate or join with pipes)
607
   return \%commands;
628
   return \%commands;