marsadm: fix transitive closure for log_purge_res()

This commit is contained in:
Thomas Schoebel-Theuer 2014-03-01 22:25:47 +01:00
parent 2ec9800d54
commit 0d5349b2ca
1 changed files with 112 additions and 29 deletions

View File

@ -550,20 +550,32 @@ sub get_minmax_replays {
my %visited_pos;
sub _visit {
my ($nr, $peer) = @_;
$nr =~ s:^0*::;
my $visit = "$nr,$peer";
$visited_pos{$visit} = 1;
}
sub _is_visited {
my ($nr, $peer) = @_;
$nr =~ s:^0*::;
my $visit = "$nr,$peer";
return $visited_pos{$visit};
}
sub _parse_pos {
my ($pos, $do_remember) = @_;
$pos =~ m/((?:log|version)-([0-9]+)-([^,]+)(?:,([0-9]+))?)/ or ldie "cannot parse '$pos'\n";
if ($do_remember) {
my $visit = "$2,$3";
$visited_pos{$visit} = 1;
}
$pos =~ m/((?:log|version)-([0-9]+)-([^,]+)(?:,([0-9]+))?)/ or lwarn "cannot parse position info '$pos'\n";
_visit($2, $3) if $do_remember;
return ($1, int($2), $3, defined($4) ? int($4) : -1);
}
sub _get_prev_pos {
my ($basedir, $nr, $host) = @_;
my $path = sprintf("$basedir/version-%09d-$host", $nr);
my $vers = get_link($path, 1);
my ($basedir, $nr, $peer, $do_remember) = @_;
my $path = sprintf("$basedir/version-%09d-$peer", $nr);
my $vers = get_link($path, 2);
_parse_pos($path, 1) if $do_remember && defined($vers) && $vers;
$vers =~ s/^.*://;
return $vers;
}
@ -647,30 +659,39 @@ sub detect_splitbrain {
}
sub _mark_path_backward {
my ($basedir, $pos) = @_;
my ($basedir, $pos, $peer, $skip_last) = @_;
my $sum = 0;
for (;;) {
my ($p, $nr, $from, $len) = _parse_pos($pos, 1);
$pos = _get_prev_pos($basedir, $nr, $from);
_visit($nr, $peer);
$pos = _get_prev_pos($basedir, $nr, $peer, 1);
last if !$pos;
# optionally don't count the last versionlink, pointing into nirvana
if (defined($skip_last) && $skip_last && $nr > 1) {
my ($p, $nr, $from, $len) = _parse_pos($pos, 0);
last if !$p;
my $next = _get_prev_pos($basedir, $nr, $peer, 1);
last if !$next;
}
$sum += $len;
}
return $sum;
}
sub _mark_path_forward {
my ($basedir, $pos) = @_;
my ($basedir, $pos, $peer) = @_;
my @list = ($pos);
while (@list) {
my %next_list;
foreach $pos (@list) {
my ($p, $nr, $from, $len) = _parse_pos($pos, 1);
my $next = sprintf("$basedir/version-%09d-*", $nr + 1);
my @candidates = glob($next);
foreach my $cand (@candidates) {
my $vers = get_link($cand, 1);
$vers =~ s/^.*://;
my ($cp, $cnr, $cfrom, $clen) = _parse_pos($vers, 0);
if (int($cnr) == int($nr) && $cfrom eq $from && $clen == $len) {
$next_list{$cand} = 1;
}
my $cand = sprintf("$basedir/version-%09d-$peer", $nr + 1);
my $vers = get_link($cand, 2);
next unless defined($vers) && $vers ne "";
$vers =~ s/^.*://;
my ($cp, $cnr, $cfrom, $clen) = _parse_pos($vers, 0);
if (int($cnr) == int($nr) && $cfrom eq $from && $clen == $len) {
$next_list{$cand} = 1;
}
}
@list = keys(%next_list);
@ -684,20 +705,82 @@ sub _mark_path_transitive {
sub log_purge_res {
my ($cmd, $res) = @_;
lwarn "DANGEROUS OPERATION: $cmd on resource '$res'\n";
my %start_logs;
my $start_count = 0;
my $basedir = "$mars/resource-$res";
my @files = glob("$basedir/{log,version}-*");
foreach my $replay (glob("$basedir/replay-*")) {
foreach my $data (glob("$basedir/{data,replay}-*")) {
$data =~ m:/(data|replay)-(.+):;
my $peer = $2;
my $replay = "$basedir/replay-$peer";
my $target = get_link($replay, 1);
_mark_path_transitive($basedir, $target);
lprint "found replay link '$replay' -> '$target'\n";
$target =~ s/,.*//;
$start_logs{$target}++;
$start_count++;
_mark_path_transitive($basedir, $target, $peer);
}
foreach my $file (@files) {
$file =~ m:/((log|version)-([0-9]+)-([^,]+)): or ldie "bad path '$file'\n";
next if (!$force && $4 ne $host);
my $visit = "$3,$4";
lprint "checking '$1'\n";
next if $visited_pos{$visit};
if (!$start_count) {
ldie "Resource contains no valid information - refusing to delete everything for safety reasons\n";
}
my %logs;
foreach my $file (glob("$basedir/version-*")) {
$file =~ m:/(version-([0-9]+)-([^,]+)): or ldie "bad path '$file'\n";
my $cand = $1;
my $nr = $2;
my $from = $3;
lprint "checking '$cand'\n";
my $vers = get_link($file, 1);
$vers =~ m/(log-[0-9]+-[^,:]+)/;
my $log = $1;
lprint " corresponding logfile is '$log'\n";
$logs{$log}++;
if (_is_visited($nr, $from)) {
lprint " ok '$cand'\n";
next;
}
if (!$force && $from ne $host) {
lprint " skipping foreign object '$cand'\n";
next;
}
lwarn "deleting foreign object from peer '$from' because you said --force\n" if $from ne $host;
_create_delete($file);
}
foreach my $file (glob("$basedir/log-*")) {
$file =~ m:/(log-[0-9]+-(.*)): or ldie "bad path '$file'\n";
my $log = $1;
my $from = $2;
lprint "checking '$log'\n";
if ($logs{$log}) {
lprint " ok '$log'\n";
$logs{$log} = -1;
next;
}
if ($start_logs{$log}) {
lprint " ok start '$log'\n";
$logs{$log} = -1;
next;
}
if (!$force && $from ne $host) {
lprint " skipping foreign object '$log'\n";
next;
}
lwarn "deleting foreign object from peer '$from' because you said --force\n" if $from ne $host;
_create_delete($file);
}
my $count = 0;
foreach my $log (sort(keys(%logs))) {
my $nr = $logs{$log};
next if $nr < 0 || -e "$basedir/$log";
lprint_stderr "info: logfile '$log' is referenced ($nr), but not present.\n";
$count++;
}
if ($count) {
lprint_stderr " Unreferenced logfiles are not necessarily bad.\n";
lprint_stderr " They can regularly appear after 'leave-resource',\n";
lprint_stderr " or 'invalidate', or after emergency mode,\n";
lprint_stderr " or after similar operations.\n";
}
finish_links();
_wait_delete();
}