From: Eric Bollengier Date: Fri, 4 Sep 2020 11:48:27 +0000 (+0200) Subject: BEE Backport regress/scripts/find_bad_jobmedia.pl X-Git-Tag: Release-11.3.2~1090 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=9ab5c871a38fdf82af2219417fe2d673af1fffe4;p=thirdparty%2Fbacula.git BEE Backport regress/scripts/find_bad_jobmedia.pl This commit is the result of the squash of the following main commits: Author: Eric Bollengier Date: Wed Oct 30 15:18:58 2013 +0100 regress: Add test to show maxuseduration problem #8412 --- diff --git a/regress/scripts/find_bad_jobmedia.pl b/regress/scripts/find_bad_jobmedia.pl new file mode 100755 index 0000000000..9dca41d4cd --- /dev/null +++ b/regress/scripts/find_bad_jobmedia.pl @@ -0,0 +1,290 @@ +#!/usr/bin/perl -w +use strict; + +=head1 NAME + + find_bad_jobmedia.pl -- Bacula System tool to detect bad JobMedia records + +=head1 SYNOPIS + + find_bad_jobmedia.pl + + --bin=/opt/bacula/bin + --conf=/opt/bacula/etc/bacula-dir.conf + + -U|--user|-u user + -D|--database|-d database + -h|--host hostname + -T|--type database type (Pg|mysql) + -P|--port database port + + --logs display logs of jobs + +=head2 VERSION + + 1.1 + +=cut + +use DBI; +use Getopt::Long qw/:config no_ignore_case/; +use Data::Dumper; +use Pod::Usage; +use POSIX qw(strftime); + +my $bin_path = "/opt/bacula/bin"; +my $dir_conf = "/opt/bacula/etc/bacula-dir.conf"; + +my $user=''; +my $pass=''; +my $host=''; +my $dbname=''; +my $dbport=''; +my $dbtype='postgresql'; +my $dsp_logs=0; + +GetOptions('bin=s' => \$bin_path, + 'conf=s' => \$dir_conf, + 'U=s' => \$user, + 'user|u=s' => \$user, + 'host|h=s' => \$host, + 'd|database|D=s' => \$dbname, + 'type|T=s' => \$dbtype, + 'port|P=i' => \$dbport, + 'logs' => \$dsp_logs, + ) || Pod::Usage::pod2usage(-exitval => 2, -verbose => 2) ; + +my %dbi; + +sub p +{ + print "\n", strftime("%H:%M:%S ", localtime); + print @_; +} + +sub connect_db +{ + my ($dbi, $user, $password) = @_; + + if (!$dbi) { + ($dbi, $user, $password) = get_bacula_db(); + } + + my $dbh; + + if ($dbi !~ /Pg|mysql|SQLite/) { + die("ERROR: It looks you have a case problem with your DBI string.\n" . + "$dbi doesn't contain Pg, mysql or SQLite"); + } + + $dbh = DBI->connect($dbi, $user, $password); + + die("ERROR: Can't connect to your database:\n$DBI::errstr\n\n" . + "You can try to run this script as 'postgres'\n" . + "For example: $0 -d bacula\n" + ) unless ($dbh); + + $dbh->{FetchHashKeyName} = 'NAME_lc'; + $dbh->{PrintError} = 1; + + return $dbh; +} + +sub get_bacula_db +{ + if ($user || $host || $dbname || $dbport) { + $dbi{db_user} = $user; + $dbi{db_address} = $host; + $dbi{db_name} = $dbname; + $dbi{db_port} = $dbport; + $dbi{db_type} = $dbtype; + + die "Unable to find database name in argument" + unless $dbi{db_name}; + + } else { + + die "ERROR: Unable to find or access $bin_path/dbcheck" + unless (-x "$bin_path/dbcheck"); + + die "ERROR: Unable to find or access $dir_conf" + unless (-r $dir_conf); + + my @conf = `$bin_path/dbcheck -B -c $dir_conf`; + if ($? != 0) { + die "ERROR: dbcheck returned an error\n@conf"; + } + + my $found=0; + + foreach my $l (@conf) { + chomp($l); + if ($l !~ /=/) { + print "Strange line on dbcheck output $l\n"; + next; + } + if ($l =~ /catalog=/) { + if ($found++ > 0) { + print "Will choose the first catalog, discarding $l\n"; + print "To migrate $l, use command line parameters\n"; + last; + } + } + my ($k, $v) = split(/=/, $l); + $dbi{$k} = $v; + } + + die "ERROR: Unable to get information from dbcheck" + unless ($dbi{db_type} and $dbi{db_name}); + } + + my $dbi_string; + if (lc($dbi{db_type}) eq 'postgresql') { + $dbi_string = "DBI:Pg"; + + } elsif (lc($dbi{db_type}) eq 'mysql') { + $dbi_string = "DBI:mysql"; + + } else { + die "Database type $dbi{db_type} is not supported by this script"; + } + + # Is present! + if ($dbi{db_name}) { + $dbi_string .= ":database=$dbi{db_name}"; + } + + if ($dbi{db_address}) { + $dbi_string .= ";host=$dbi{db_address}"; + } + + if ($dbi{db_port}) { + $dbi_string .= ";port=$dbi{db_port}"; + } + + if ($dbi{db_socket}) { + $dbi_string .= ";host=$dbi{db_socket}"; + } + + return ($dbi_string, $dbi{db_user}, $dbi{db_password}); +} + +my $ret; +my $dbh = connect_db(); + +p "INFO: Connexion to the catalog OK\n"; + +################################################################ + +my @suspectfields = qw(JobMediaId JobId MediaId FirstIndex + LastIndex StartFile EndFile StartBlock + EndBlock VolIndex); + +my $suspect = $dbh->selectall_arrayref(" +SELECT " . join(",", @suspectfields) . " + FROM JobMedia + WHERE EndFile <= StartFile AND EndBlock < StartBlock + ORDER BY JobId, VolIndex"); + +if (scalar(@$suspect) == 0) { + p "INFO: No problem detected\n"; + exit 0; +} + +print "\n================================================================\n"; +p "INFO: Found " . scalar(@$suspect) . " suspect records\n"; +print join("\t", @suspectfields), "\n"; +foreach my $row (@$suspect) { + print join("\t", @$row), "\n"; +} + +my $jobids = join(",", map { $_->[1] } @$suspect); + +print "\n================================================================\n"; +p "INFO: Dumping JobMedia for suspicious jobs\n"; + +my @jobmediafields = qw(JobMediaId JobId MediaId FirstIndex + LastIndex StartFile EndFile StartBlock + EndBlock VolIndex); +my $jobmedia = $dbh->selectall_arrayref(" +SELECT " . join(",", @jobmediafields) . " + FROM JobMedia + WHERE JobId IN ($jobids) + ORDER BY JobId, VolIndex +"); + +print join("\t", @jobmediafields), "\n"; +foreach my $row2 (@$jobmedia) { + print join("\t", @$row2), "\n"; +} + +print "\n================================================================\n"; +p "INFO: Dumping Jobs information\n"; + +my @jobfields = qw(JobId Job Name Type ClientId StartTime EndTime JobFiles + JobBytes JobStatus); + +my $jobs = $dbh->selectall_arrayref(" +SELECT " . join(",", @jobfields) . " + FROM Job + WHERE JobId IN ($jobids) + ORDER BY JobId +"); + +my $nbjob = scalar(@$jobs); + +print join("\t", @jobfields), "\n"; +foreach my $row2 (@$jobs) { + print join("\t", @$row2), "\n"; +} + +print "\n================================================================\n"; +p "INFO: Dumping info about Media\n"; + +my @mediaidfields = qw(MediaId VolumeName LastWritten VolBlocks VolBytes + VolFiles VolJobs VolRetention VolStatus); + +my $mediaids = join(",", map { $_->[2] } @$suspect); + +my $media = $dbh->selectall_arrayref(" +SELECT " . join(",", @mediaidfields) . " + FROM Media + WHERE MediaId IN ($mediaids) + ORDER BY MediaId +"); + +print join("\t", @mediaidfields), "\n"; +foreach my $row2 (@$media) { + print join("\t", map { defined $_ ? $_ : 'null' } @$row2), "\n"; +} + +# We display logs if requested or if we don't have too much jobs involved +if ($dsp_logs || $nbjob < 15) { + print "\n================================================================\n"; + p "INFO: Dumping logs information for all jobs between start and end time\n"; + my @logfields = qw/Time LogText/; + foreach my $job (@$jobs) { + p "INFO: Dumping for JobId $job->[0]\n"; + print join("\t", @logfields), "\n"; + my $log = $dbh->selectall_arrayref(" +SELECT " . join(",", @logfields) . " + FROM Log + WHERE Time >= '$job->[5]' and Time <= '$job->[6]' + ORDER BY LogId +"); + # If we display lines automatically, ensure + # we don't have too much lines + if ($dsp_logs || scalar(@$log) < 1000) { + foreach my $row2 (@$log) { + print join("\t", @$row2); + } + } else { + print "Too much lines... ", scalar(@$log), "\n"; + } + } +} + +################################################################ + +exit 0; +