/[BackupPC]/trunk/bin/BackupPC_incPartsUpdate
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Diff of /trunk/bin/BackupPC_incPartsUpdate

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 264 by dpavlin, Tue Dec 13 00:10:47 2005 UTC revision 323 by dpavlin, Tue Jan 31 11:11:37 2006 UTC
# Line 16  use Archive::Tar::Streamed; Line 16  use Archive::Tar::Streamed;
16  use Algorithm::Diff;  use Algorithm::Diff;
17  use Getopt::Std;  use Getopt::Std;
18  use File::Slurp;  use File::Slurp;
19    use File::Pid;
20    
21    =head1 NAME
22    
23    BackupPC_incPartsUpdate
24    
25    =head1 DESCRIPTION
26    
27    Create C<.tar.gz> increments on disk calling C<BackupPC_tarIncCreate>.
28    
29    Following options are supported (but all are optional):
30    
31    =over 4
32    
33    =item -h hostname
34    
35    Update parts for just single C<hostname>
36    
37    =item -c
38    
39    Force check for tar archives which exist on disk
40    
41    =item -d
42    
43    Turn debugging output
44    
45    =back
46    
47    =cut
48    
49    my %opt;
50    getopts("cdh:", \%opt );
51    
52    my $debug = $opt{d};
53    my $check = $opt{c} && print STDERR "NOTICE: tar archive check forced\n";
54    
55    my $pid_path = abs_path($0);
56    $pid_path =~ s/\W+/_/g;
57    
58    my $pidfile = new File::Pid({
59            file => "/tmp/$pid_path",
60    });
61    
62    if (my $pid = $pidfile->running ) {
63            die "$0 already running: $pid\n";
64    } elsif ($pidfile->pid ne $$) {
65            $pidfile->remove;
66            $pidfile = new File::Pid;
67    }
68    
69    print STDERR "$0 using pid ",$pidfile->pid," file ",$pidfile->file,"\n";
70    $pidfile->write;
71    
72  my $bpc = BackupPC::Lib->new || die "can't create BackupPC::Lib";  my $bpc = BackupPC::Lib->new || die "can't create BackupPC::Lib";
73  my %Conf = $bpc->Conf();  my %Conf = $bpc->Conf();
# Line 34  foreach my $c (qw/gzip md5sum/) { Line 86  foreach my $c (qw/gzip md5sum/) {
86          $bin->{$c} = which($c) || die "$0 needs $c, install it\n";          $bin->{$c} = which($c) || die "$0 needs $c, install it\n";
87  }  }
88    
 my %opt;  
 getopts("cd", \%opt );  
   
 my $debug = $opt{d};  
 my $check = $opt{c} && print STDERR "NOTICE: tar archive check forced\n";  
   
89  $|=1;  $|=1;
90    
91  my $start_t = time();  my $start_t = time();
# Line 84  sub get_backup_id($$$) { Line 130  sub get_backup_id($$$) {
130                  FROM backups                  FROM backups
131                  INNER JOIN shares       ON backups.shareID=shares.ID                  INNER JOIN shares       ON backups.shareID=shares.ID
132                  INNER JOIN hosts        ON backups.hostID = hosts.ID                  INNER JOIN hosts        ON backups.hostID = hosts.ID
133                  where hosts.name = ? and shares.name = ? and backups.num = ?                  WHERE hosts.name = ? and shares.name = ? and backups.num = ?
134          });          });
135          $sth->execute($host, $share, $num);          $sth->execute($host, $share, $num);
136          my ($id) = $sth->fetchrow_array;          my ($id) = $sth->fetchrow_array;
# Line 96  sub get_backup_id($$$) { Line 142  sub get_backup_id($$$) {
142          return $id;          return $id;
143  }  }
144    
145    sub backup_inc_deleted($) {
146            my $backup_id = shift;
147            my $sth_inc_deleted = $dbh->prepare(qq{
148                    update backups set
149                            inc_deleted = true,
150                            parts = 0
151                    where id = ?
152            });
153            $sth_inc_deleted->execute($backup_id);
154    }
155    
156  sub tar_check($$$$) {  sub tar_check($$$$) {
157          my ($host,$share,$num,$filename) = @_;          my ($host,$share,$num,$filename) = @_;
158    
159            my $t = time();
160            print curr_time, " check $host:$share#$num -> $filename";
161    
162            # depending on expected returned value this is used like:
163            # my $uncompress_size = get_gzip_size('/full/path/to.gz');
164            # my ($compress_size, $uncompress_size) = get_gzip_size('/path.gz');
165            sub get_gzip_size($) {
166                    my $filename = shift;
167                    die "file $filename problem: $!" unless (-r $filename);
168                    open(my $gzip, $bin->{gzip}." -l $filename |") || die "can't gzip -l $filename: $!";
169                    my $line = <$gzip>;
170                    chomp($line);
171                    $line = <$gzip> if ($line =~ /^\s+compressed/);
172    
173                    my ($comp, $uncomp) = (0,0);
174    
175                    if ($line =~ m/^\s+(\d+)\s+(\d+)\s+\d+\.\d+/) {
176                            if (wantarray) {
177                                    return [ $1, $2 ];
178                            } else {
179                                    return $2;
180                            }
181                    } else {
182                            die "can't find size in line: $line";
183                    }
184            }
185    
186          sub check_part {          sub check_part {
187                  my ($host, $share, $num, $part_nr, $tar_size, $size, $md5, $items) = @_;                  my ($host, $share, $num, $part_nr, $tar_size, $size, $md5, $items) = @_;
188                  my $backup_id = get_backup_id($host, $share, $num);                  my $backup_id = get_backup_id($host, $share, $num);
# Line 114  sub tar_check($$$$) { Line 197  sub tar_check($$$$) {
197    
198                  if (my $row = $sth_md5->fetchrow_hashref) {                  if (my $row = $sth_md5->fetchrow_hashref) {
199                          return if (                          return if (
200                                  $row->{tar_size} == $tar_size &&                                  $row->{tar_size} >= $tar_size &&
201                                  $row->{size} == $size &&                                  $row->{size} == $size &&
202                                  $row->{md5} eq $md5 &&                                  $row->{md5} eq $md5 &&
203                                  $row->{items} == $items                                  $row->{items} == $items
204                          );                          );
205                          print STDERR "# deleting invalid row $row->{id}\n" if ($opt{d});                          print ", deleting invalid backup_parts $row->{id}";
206                          $dbh->do(qq{ delete from backup_parts where id = $row->{id} });                          $dbh->do(qq{ delete from backup_parts where id = $row->{id} });
207                  }                  }
208                  print STDERR "# inserting new backup_part row\n";                  print ", inserting new";
209                  my $sth_insert = $dbh->prepare(qq{                  my $sth_insert = $dbh->prepare(qq{
210                          insert into backup_parts (                          insert into backup_parts (
211                                  backup_id,                                  backup_id,
# Line 138  sub tar_check($$$$) { Line 221  sub tar_check($$$$) {
221                  $dbh->commit;                  $dbh->commit;
222          }          }
223    
         if ($debug) {  
                 print STDERR " {{ CHECK: ${host}:${share}#${num} and $filename";  
         } else {  
                 print " check";  
         }  
   
224          my @tar_parts;          my @tar_parts;
225    
226          if (-d "$tar_dir/$filename") {          if (-d "$tar_dir/$filename") {
227                  print STDERR " multi-part" if ($opt{d});                  print ", multi-part";
228                  opendir(my $dir, "$tar_dir/$filename") || die "can't readdir $tar_dir/$filename: $!";                  opendir(my $dir, "$tar_dir/$filename") || die "can't readdir $tar_dir/$filename: $!";
229                  @tar_parts = map { my $p = $_; $p =~ s#^#${filename}/#; $p } grep { !/^\./ && !/md5/ && -f "$tar_dir/$filename/$_" } readdir($dir);                  @tar_parts = map { my $p = $_; $p =~ s#^#${filename}/#; $p } grep { !/^\./ && !/md5/ && -f "$tar_dir/$filename/$_" } readdir($dir);
230                  closedir($dir);                  closedir($dir);
# Line 162  sub tar_check($$$$) { Line 239  sub tar_check($$$$) {
239    
240          my $backup_part;          my $backup_part;
241    
242          print " reading";          print " reading" if ($opt{d});
243    
244          foreach my $tarfilename (@tar_parts) {          foreach my $tarfilename (@tar_parts) {
245    
246                  print STDERR " $tarfilename" if ($debug);                  print "\n\t- $tarfilename";
247    
248                  my $path = "$tar_dir/$tarfilename";                  my $path = "$tar_dir/$tarfilename";
                 my $md5_path = $path;  
                 $md5_path =~ s/\.tar\.gz$/.md5/ || die "can't create md5 filename from $md5_path";  
                 if (! -e $md5_path) {  
                         print ", creating md5";  
                         system( $bin->{md5sum} . " $path > $md5_path") == 0 or die "can't create md5 $path: $!";  
                 }  
249    
250                  my $md5 = read_file( $md5_path ) || die "can't read md5sum file $md5_path: $!";                  my $size = (stat( $path ))[7] || die "can't stat $path: $!";
251    
252                  my $part_nr = 1;                  if ($size > $Conf{MaxArchiveSize}) {
253                  $part_nr = $1 if ($tarfilename =~ m#/(\d+)\.tar\.gz#);                          print ", part bigger than media $size > $Conf{MaxArchiveSize}\n";
254                            return 0;
255                    }
256    
257                  my $size = (stat( "$tar_dir/$tarfilename" ))[7] || die "can't stat $tar_dir/$tarfilename";                  print ", $size bytes";
258    
259                  open(my $fh, "gzip -cd $tar_dir/$tarfilename |") or die "can't open $tar_dir/$tarfilename: $!";  
260                    open(my $fh, "gzip -cd $path |") or die "can't open $path: $!";
261                  binmode($fh);                  binmode($fh);
262                  my $tar = Archive::Tar::Streamed->new($fh);                  my $tar = Archive::Tar::Streamed->new($fh);
263    
264                  my $tar_size = 0;                  my $tar_size_inarc = 0;
265                  my $items = 0;                  my $items = 0;
266    
267                  while(my $entry = $tar->next) {                  while(my $entry = $tar->next) {
268                          push @tar_files, $entry->name;                          push @tar_files, $entry->name;
269                          $items++;                          $items++;
270                          $tar_size += $entry->size;                          $tar_size_inarc += $entry->size;
271    
272                            if ($tar_size_inarc > $Conf{MaxArchiveFileSize}) {
273                                    print ", part $tarfilename is too big $tar_size_inarc > $Conf{MaxArchiveFileSize}\n";
274                                    return 0;
275                            }
276    
277                  }                  }
278    
279                  if ($tar_size > $Conf{MaxArchiveFileSize}) {                  close($fh);
280                          print STDERR " part too big $tar_size > $Conf{MaxArchiveFileSize} }}" if ($debug);  
281                          $same = 0;                  print ", $items items";
282                          last;  
283                  } elsif ($size > $Conf{MaxArchiveSize}) {                  if ($tar_size_inarc == 0 && $items == 0) {
284                          print STDERR " part bigger than media $size > $Conf{MaxArchiveSize} }}" if ($debug);                          print ", EMPTY tar\n";
285                          $same = 0;  
286                          last;                          my $backup_id = get_backup_id($host, $share, $num);
287                            backup_inc_deleted( $backup_id );
288    
289                            $dbh->commit;
290    
291                            return 1;
292                    }
293    
294                    my $tar_size = get_gzip_size( $path );
295    
296                    # real tar size is bigger because of padding    
297                    if ($tar_size_inarc > $tar_size) {
298                            print ", size of files in tar ($tar_size_inarc) bigger than whole tar ($tar_size)!\n";
299                            return 0;
300                    }
301    
302                    #
303                    # check if md5 exists, and if not, create one
304                    #
305    
306                    my $md5_path = $path;
307                    $md5_path =~ s/\.tar\.gz$/.md5/ || die "can't create md5 filename from $md5_path";
308                    if (! -e $md5_path || -z $md5_path) {
309                            print ", creating md5";
310                            system( $bin->{md5sum} . " $path > $md5_path") == 0 or die "can't create md5 $path: $!";
311                    } else {
312                            ## FIXME check if existing md5 is valid
313                  }                  }
314    
315                    my $md5 = read_file( $md5_path ) || die "can't read md5sum file $md5_path: $!";
316                    $md5 =~ s#\s.*$##;
317    
318                    # extract part number from filename
319                    my $part_nr = 1;
320                    $part_nr = $1 if ($tarfilename =~ m#/(\d+)\.tar\.gz#);
321    
322                    #
323                    # finally, check if backup_parts table in database is valid
324                    #
325    
326                  check_part($host, $share, $num, $part_nr, $tar_size, $size, $md5, $items);                  check_part($host, $share, $num, $part_nr, $tar_size, $size, $md5, $items);
327          }          }
328    
# Line 213  sub tar_check($$$$) { Line 330  sub tar_check($$$$) {
330          return $same unless($same);          return $same unless($same);
331    
332          @tar_files = sort @tar_files;          @tar_files = sort @tar_files;
333          print STDERR " ",($#tar_files + 1), " files" if ($debug);          print "\n\t",($#tar_files + 1), " tar files";
   
         print STDERR ", database" if ($debug);  
334    
335          my $sth = $dbh->prepare(qq{          my $sth = $dbh->prepare(qq{
336                  SELECT path,type                  SELECT path,type
# Line 234  sub tar_check($$$$) { Line 349  sub tar_check($$$$) {
349                  push @db_files, $path;                  push @db_files, $path;
350          }          }
351    
352          print STDERR " ",($#db_files + 1), " files, diff" if ($debug);          print " ",($#db_files + 1), " database files, diff";
353    
354          @db_files = sort @db_files;          @db_files = sort @db_files;
355    
356          if ($#tar_files != $#db_files) {          if ($#tar_files != $#db_files) {
357                  $same = 0;                  $same = 0;
358                  print STDERR " NUMBER" if ($debug);                  print " NUMBER";
359          } else {          } else {
360                  my $diff = Algorithm::Diff->new(\@tar_files, \@db_files);                  my $diff = Algorithm::Diff->new(\@tar_files, \@db_files);
361                  while ( $diff->Next() ) {                  while ( $diff->Next() ) {
# Line 251  sub tar_check($$$$) { Line 366  sub tar_check($$$$) {
366                  }                  }
367          }          }
368    
369          print " ",($same ? 'ok' : 'DIFFERENT');          print " ",($same ? 'ok' : 'DIFFERENT'),
370          print STDERR " }} " if ($debug);                  ", dur: ",fmt_time(time() - $t), "\n";
371    
372          return $same;          return $same;
373  }  }
# Line 267  select Line 382  select
382          hosts.name as host,          hosts.name as host,
383          shares.name as share,          shares.name as share,
384          backups.num as num,          backups.num as num,
385            backups.date,
386          inc_size,          inc_size,
387          parts          parts,
388            count(backup_parts.backup_id) as backup_parts
389  from backups  from backups
390          join shares on backups.hostid = shares.hostid          join shares on backups.hostid = shares.hostid
391                  and shares.id = backups.shareid                  and shares.id = backups.shareid
392          join hosts on shares.hostid = hosts.id          join hosts on shares.hostid = hosts.id
393  where not inc_deleted          full outer join backup_parts on backups.id = backup_parts.backup_id
394    where not inc_deleted and backups.size > 0
395    group by backups.id, hosts.name, shares.name, backups.num, backups.date, inc_size, parts, backup_parts.backup_id
396  order by backups.date  order by backups.date
397    
398  } );  } );
# Line 282  $sth->execute(); Line 401  $sth->execute();
401  my $num_backups = $sth->rows;  my $num_backups = $sth->rows;
402  my $curr_backup = 1;  my $curr_backup = 1;
403    
404    if ($opt{h}) {
405            warn "making increments just for host $opt{h}\n";
406    }
407    
408  while (my $row = $sth->fetchrow_hashref) {  while (my $row = $sth->fetchrow_hashref) {
409    
410            if ($opt{h} && $row->{host} ne $opt{h}) {
411                    warn "skipped $row->{host}\n" if ($debug);
412                    next;
413            }
414    
415            $curr_backup++;
416    
417          my $tar_file = BackupPC::SearchLib::getGzipName($row->{'host'}, $row->{'share'}, $row->{'num'});          my $tar_file = BackupPC::SearchLib::getGzipName($row->{'host'}, $row->{'share'}, $row->{'num'});
418    
419          # this will return -1 if file doesn't exist          # this will return -1 if file doesn't exist
# Line 290  while (my $row = $sth->fetchrow_hashref) Line 421  while (my $row = $sth->fetchrow_hashref)
421    
422          print "# size: $size backup.size: ", $row->{inc_size},"\n" if ($opt{d});          print "# size: $size backup.size: ", $row->{inc_size},"\n" if ($opt{d});
423    
424          if ( $row->{'inc_size'} != -1 && $size != -1 && $row->{'inc_size'} == $size) {          if ( $row->{'inc_size'} != -1 && $size != -1 && $row->{'inc_size'} >= $size && $row->{parts} == $row->{backup_parts}) {
425                  if ($check) {                  if ($check) {
426                          tar_check($row->{'host'}, $row->{'share'}, $row->{'num'}, $tar_file) && next;                          tar_check($row->{'host'}, $row->{'share'}, $row->{'num'}, $tar_file) && next;
427                  } else {                  } else {
# Line 298  while (my $row = $sth->fetchrow_hashref) Line 429  while (my $row = $sth->fetchrow_hashref)
429                  }                  }
430          }          }
431    
432          print curr_time, " $curr_backup/$num_backups ", $row->{'host'}, ":", $row->{'share'}, " #", $row->{'num'}, " -> $tar_file";          print curr_time, " creating $curr_backup/$num_backups ", $row->{host}, ":", $row->{share}, " #", $row->{num},
433          $curr_backup++;                  " ", strftime('%Y-%m-%d', localtime($row->{date})), " -> $tar_file";
434    
435          my $t = time();          my $t = time();
436    
437          # re-create archive?          # re-create archive?
438          my $cmd = qq{ $tarIncCreate -h "$row->{'host'}" -s "$row->{'share'}" -n $row->{'num'} -f };          my $cmd = qq[ $tarIncCreate -h "$row->{host}" -s "$row->{share}" -n $row->{num} -f ];
439          print STDERR "## $cmd\n" if ($debug);          print STDERR "## $cmd\n" if ($debug);
440    
441          if (system($cmd) != 0) {          if (system($cmd) != 0) {
442                  print STDERR " FAILED";                  print STDERR " FAILED, marking this backup deleted";
443                    backup_inc_deleted( $row->{backup_id} );
444          }          }
445    
446          print ", dur: ",fmt_time(time() - $t), "\n";          print ", dur: ",fmt_time(time() - $t), "\n";

Legend:
Removed from v.264  
changed lines
  Added in v.323

  ViewVC Help
Powered by ViewVC 1.1.26