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

Contents of /trunk/bin/BackupPC_updatedb

Parent Directory Parent Directory | Revision Log Revision Log


Revision 315 - (show annotations)
Sun Jan 29 18:08:52 2006 UTC (18 years, 3 months ago) by dpavlin
File size: 17126 byte(s)
better output for -i and -j

1 #!/usr/local/bin/perl -w
2
3 use strict;
4 use lib "__INSTALLDIR__/lib";
5
6 use DBI;
7 use BackupPC::Lib;
8 use BackupPC::View;
9 use Data::Dumper;
10 use Getopt::Std;
11 use Time::HiRes qw/time/;
12 use File::Pid;
13 use POSIX qw/strftime/;
14 use BackupPC::SearchLib;
15 use Cwd qw/abs_path/;
16
17 use constant BPC_FTYPE_DIR => 5;
18 use constant EST_CHUNK => 4096;
19
20 # daylight saving time change offset for 1h
21 my $dst_offset = 60 * 60;
22
23 my $debug = 0;
24 $|=1;
25
26 my $start_t = time();
27
28 my $pid_path = abs_path($0);
29 $pid_path =~ s/\W+/_/g;
30
31 my $pidfile = new File::Pid({
32 file => "/tmp/$pid_path",
33 });
34
35 if (my $pid = $pidfile->running ) {
36 die "$0 already running: $pid\n";
37 } elsif ($pidfile->pid ne $$) {
38 $pidfile->remove;
39 $pidfile = new File::Pid;
40 }
41 print STDERR "$0 using pid ",$pidfile->pid," file ",$pidfile->file,"\n";
42 $pidfile->write;
43
44 my $t_fmt = '%Y-%m-%d %H:%M:%S';
45
46 my $hosts;
47 my $bpc = BackupPC::Lib->new || die;
48 my %Conf = $bpc->Conf();
49 my $TopDir = $bpc->TopDir();
50 my $beenThere = {};
51
52 my $dsn = $Conf{SearchDSN} || die "Need SearchDSN in config.pl\n";
53 my $user = $Conf{SearchUser} || '';
54
55 my $index_node_url = $Conf{HyperEstraierIndex};
56
57 my $dbh = DBI->connect($dsn, $user, "", { RaiseError => 1, AutoCommit => 0 });
58
59 my %opt;
60
61 if ( !getopts("cdm:v:ijfq", \%opt ) ) {
62 print STDERR <<EOF;
63 usage: $0 [-c|-d] [-m num] [-v|-v level] [-i|-j|-f]
64
65 Options:
66 -c create database on first use
67 -d delete database before import
68 -m num import just num increments for one host
69 -v num set verbosity (debug) level (default $debug)
70 -i update Hyper Estraier full text index
71 -j update full text, don't check existing files
72 -f don't do anything with full text index
73 -q be quiet for hosts without changes
74
75 Option -j is variation on -i. It will allow faster initial creation
76 of full-text index from existing database.
77
78 Option -f will create database which is out of sync with full text index. You
79 will have to re-run $0 with -i to fix it.
80
81 EOF
82 exit 1;
83 }
84
85 if ($opt{v}) {
86 print "Debug level at $opt{v}\n";
87 $debug = $opt{v};
88 } elsif ($opt{f}) {
89 print "WARNING: disabling full-text index update. You need to re-run $0 -j !\n";
90 $index_node_url = undef;
91 }
92
93 #---- subs ----
94
95 sub fmt_time {
96 my $t = shift || return;
97 my $out = "";
98 my ($ss,$mm,$hh) = gmtime($t);
99 $out .= "${hh}h" if ($hh);
100 $out .= sprintf("%02d:%02d", $mm,$ss);
101 return $out;
102 }
103
104 sub curr_time {
105 return strftime($t_fmt,localtime());
106 }
107
108 my $hest_node;
109
110 sub hest_update {
111
112 my ($host_id, $share_id, $num) = @_;
113
114 my $skip_check = $opt{j} && print STDERR "Skipping check for existing files -- this should be used only with initital import\n";
115
116 unless (defined($index_node_url)) {
117 print STDERR "HyperEstraier support not enabled in configuration\n";
118 $index_node_url = 0;
119 return;
120 }
121
122 print curr_time," updating Hyper Estraier:";
123
124 my $t = time();
125
126 my $offset = 0;
127 my $added = 0;
128
129 print " opening index $index_node_url";
130 if ($index_node_url) {
131 $hest_node ||= Search::Estraier::Node->new(
132 url => $index_node_url,
133 user => 'admin',
134 passwd => 'admin',
135 croak_on_error => 1,
136 );
137 print " via node URL";
138 } else {
139 die "don't know how to use Hyper Estraier Index $index_node_url";
140 }
141
142 my $results = 0;
143
144 do {
145
146 my $where = '';
147 my @data;
148 if (defined($host_id) && defined($share_id) && defined($num)) {
149 $where = qq{
150 WHERE
151 hosts.id = ? AND
152 shares.id = ? AND
153 files.backupnum = ?
154 };
155 @data = ( $host_id, $share_id, $num );
156 }
157
158 my $limit = sprintf('LIMIT '.EST_CHUNK.' OFFSET %d', $offset);
159
160 my $sth = $dbh->prepare(qq{
161 SELECT
162 files.id AS fid,
163 hosts.name AS hname,
164 shares.name AS sname,
165 -- shares.share AS sharename,
166 files.backupnum AS backupnum,
167 -- files.name AS filename,
168 files.path AS filepath,
169 files.date AS date,
170 files.type AS type,
171 files.size AS size,
172 files.shareid AS shareid,
173 backups.date AS backup_date
174 FROM files
175 INNER JOIN shares ON files.shareID=shares.ID
176 INNER JOIN hosts ON hosts.ID = shares.hostID
177 INNER JOIN backups ON backups.num = files.backupNum and backups.hostID = hosts.ID AND backups.shareID = shares.ID
178 $where
179 $limit
180 });
181
182 $sth->execute(@data);
183 $results = $sth->rows;
184
185 if ($results == 0) {
186 print " - no new files\n";
187 return;
188 } else {
189 print "...";
190 }
191
192 sub fmt_date {
193 my $t = shift || return;
194 my $iso = BackupPC::Lib::timeStamp($t);
195 $iso =~ s/\s/T/;
196 return $iso;
197 }
198
199 while (my $row = $sth->fetchrow_hashref()) {
200
201 my $uri = $row->{hname} . ':' . $row->{sname} . '#' . $row->{backupnum} . ' ' . $row->{filepath};
202 unless ($skip_check) {
203 my $id = $hest_node->uri_to_id($uri);
204 next if ($id && $id == -1);
205 }
206
207 # create a document object
208 my $doc = Search::Estraier::Document->new;
209
210 # add attributes to the document object
211 $doc->add_attr('@uri', $uri);
212
213 foreach my $c (@{ $sth->{NAME} }) {
214 print STDERR "attr $c = $row->{$c}\n" if ($debug > 2);
215 $doc->add_attr($c, $row->{$c}) if (defined($row->{$c}));
216 }
217
218 #$doc->add_attr('@cdate', fmt_date($row->{'date'}));
219
220 # add the body text to the document object
221 my $path = $row->{'filepath'};
222 $doc->add_text($path);
223 $path =~ s/(.)/$1 /g;
224 $doc->add_hidden_text($path);
225
226 print STDERR $doc->dump_draft,"\n" if ($debug > 1);
227
228 # register the document object to the database
229 if ($hest_node) {
230 $hest_node->put_doc($doc);
231 } else {
232 die "not supported";
233 }
234 $added++;
235 }
236
237 print "$added";
238
239 $offset += EST_CHUNK;
240
241 } while ($results == EST_CHUNK);
242
243 my $dur = (time() - $t) || 1;
244 printf(" [%.2f/s dur: %s]\n",
245 ( $added / $dur ),
246 fmt_time($dur)
247 );
248 }
249
250 #---- /subs ----
251
252
253 ## update index ##
254 if ( ( $opt{i} || $opt{j} ) && !$opt{c} ) {
255 # update all
256 print "force update of Hyper Estraier index ";
257 print "by -i flag" if ($opt{i});
258 print "by -j flag" if ($opt{j});
259 print "\n";
260 hest_update();
261 }
262
263 ## create tables ##
264 if ($opt{c}) {
265 sub do_index {
266 my $index = shift || return;
267 my ($table,$col,$unique) = split(/:/, $index);
268 $unique ||= '';
269 $index =~ s/\W+/_/g;
270 print "$index on $table($col)" . ( $unique ? "u" : "" ) . " ";
271 $dbh->do(qq{ create $unique index $index on $table($col) });
272 }
273
274 print "creating tables...\n";
275
276 $dbh->do( qq{
277 create table hosts (
278 ID SERIAL PRIMARY KEY,
279 name VARCHAR(30) NOT NULL,
280 IP VARCHAR(15)
281 );
282
283 create table shares (
284 ID SERIAL PRIMARY KEY,
285 hostID INTEGER NOT NULL references hosts(id),
286 name VARCHAR(30) NOT NULL,
287 share VARCHAR(200) NOT NULL
288 );
289
290 create table dvds (
291 ID SERIAL PRIMARY KEY,
292 num INTEGER NOT NULL,
293 name VARCHAR(255) NOT NULL,
294 mjesto VARCHAR(255)
295 );
296
297 create table backups (
298 id serial,
299 hostID INTEGER NOT NULL references hosts(id),
300 num INTEGER NOT NULL,
301 date integer NOT NULL,
302 type CHAR(4) not null,
303 shareID integer not null references shares(id),
304 size bigint not null,
305 inc_size bigint not null default -1,
306 inc_deleted boolean default false,
307 parts integer not null default 1,
308 PRIMARY KEY(id)
309 );
310
311 create table files (
312 ID SERIAL,
313 shareID INTEGER NOT NULL references shares(id),
314 backupNum INTEGER NOT NULL,
315 name VARCHAR(255) NOT NULL,
316 path VARCHAR(255) NOT NULL,
317 date integer NOT NULL,
318 type INTEGER NOT NULL,
319 size bigint NOT NULL,
320 primary key(id)
321 );
322
323 create table archive (
324 id serial,
325 dvd_nr int not null,
326 total_size bigint default -1,
327 note text,
328 username varchar(20) not null,
329 date timestamp default now(),
330 primary key(id)
331 );
332
333 create table archive_backup (
334 archive_id int not null references archive(id) on delete cascade,
335 backup_id int not null references backups(id),
336 primary key(archive_id, backup_id)
337 );
338
339 create table archive_burned (
340 archive_id int references archive(id),
341 date timestamp default now(),
342 part int not null default 1,
343 copy int not null default 1,
344 iso_size bigint default -1
345 );
346
347 create table backup_parts (
348 id serial,
349 backup_id int references backups(id),
350 part_nr int not null check (part_nr > 0),
351 tar_size bigint not null check (tar_size > 0),
352 size bigint not null check (size > 0),
353 md5 text not null,
354 items int not null check (items > 0),
355 date timestamp default now(),
356 primary key(id)
357 );
358 });
359
360 print "creating indexes: ";
361
362 foreach my $index (qw(
363 hosts:name
364 backups:hostID
365 backups:num
366 backups:shareID
367 shares:hostID
368 shares:name
369 files:shareID
370 files:path
371 files:name
372 files:date
373 files:size
374 archive:dvd_nr
375 archive_burned:archive_id
376 backup_parts:backup_id,part_nr
377 )) {
378 do_index($index);
379 }
380
381 print " creating sequence: ";
382 foreach my $seq (qw/dvd_nr/) {
383 print "$seq ";
384 $dbh->do( qq{ CREATE SEQUENCE $seq } );
385 }
386
387
388 print "...\n";
389
390 $dbh->commit;
391
392 }
393
394 ## delete data before inseting ##
395 if ($opt{d}) {
396 print "deleting ";
397 foreach my $table (qw(files dvds backups shares hosts)) {
398 print "$table ";
399 $dbh->do(qq{ DELETE FROM $table });
400 }
401 print " done...\n";
402
403 $dbh->commit;
404 }
405
406 ## insert new values ##
407
408 # get hosts
409 $hosts = $bpc->HostInfoRead();
410 my $hostID;
411 my $shareID;
412
413 my $sth;
414
415 $sth->{insert_hosts} = $dbh->prepare(qq{
416 INSERT INTO hosts (name, IP) VALUES (?,?)
417 });
418
419 $sth->{hosts_by_name} = $dbh->prepare(qq{
420 SELECT ID FROM hosts WHERE name=?
421 });
422
423 $sth->{backups_count} = $dbh->prepare(qq{
424 SELECT COUNT(*)
425 FROM backups
426 WHERE hostID=? AND num=? AND shareid=?
427 });
428
429 $sth->{insert_backups} = $dbh->prepare(qq{
430 INSERT INTO backups (hostID, num, date, type, shareid, size)
431 VALUES (?,?,?,?,?,-1)
432 });
433
434 $sth->{update_backups_size} = $dbh->prepare(qq{
435 UPDATE backups SET size = ?
436 WHERE hostID = ? and num = ? and date = ? and type =? and shareid = ?
437 });
438
439 $sth->{insert_files} = $dbh->prepare(qq{
440 INSERT INTO files
441 (shareID, backupNum, name, path, date, type, size)
442 VALUES (?,?,?,?,?,?,?)
443 });
444
445 my @hosts = keys %{$hosts};
446 my $host_nr = 0;
447
448 foreach my $host_key (@hosts) {
449
450 my $hostname = $hosts->{$host_key}->{'host'} || die "can't find host for $host_key";
451
452 $sth->{hosts_by_name}->execute($hosts->{$host_key}->{'host'});
453
454 unless (($hostID) = $sth->{hosts_by_name}->fetchrow_array()) {
455 $sth->{insert_hosts}->execute(
456 $hosts->{$host_key}->{'host'},
457 $hosts->{$host_key}->{'ip'}
458 );
459
460 $hostID = $dbh->last_insert_id(undef,undef,'hosts',undef);
461 }
462
463 $host_nr++;
464 # get backups for a host
465 my @backups = $bpc->BackupInfoRead($hostname);
466 my $incs = scalar @backups;
467
468 my $host_header = sprintf("host %s [%d/%d]: %d increments\n",
469 $hosts->{$host_key}->{'host'},
470 $host_nr,
471 ($#hosts + 1),
472 $incs
473 );
474 print $host_header unless ($opt{q});
475
476 my $inc_nr = 0;
477 $beenThere = {};
478
479 foreach my $backup (@backups) {
480
481 $inc_nr++;
482 last if ($opt{m} && $inc_nr > $opt{m});
483
484 my $backupNum = $backup->{'num'};
485 my @backupShares = ();
486
487 my $share_header = sprintf("%-10s %2d/%-2d #%-2d %s %5s/%5s files (date: %s dur: %s)\n",
488 $hosts->{$host_key}->{'host'},
489 $inc_nr, $incs, $backupNum,
490 $backup->{type} || '?',
491 $backup->{nFilesNew} || '?', $backup->{nFiles} || '?',
492 strftime($t_fmt,localtime($backup->{startTime})),
493 fmt_time($backup->{endTime} - $backup->{startTime})
494 );
495 print $share_header unless ($opt{q});
496
497 my $files = BackupPC::View->new($bpc, $hostname, \@backups, 1);
498 foreach my $share ($files->shareList($backupNum)) {
499
500 my $t = time();
501
502 $shareID = getShareID($share, $hostID, $hostname);
503
504 $sth->{backups_count}->execute($hostID, $backupNum, $shareID);
505 my ($count) = $sth->{backups_count}->fetchrow_array();
506 # skip if allready in database!
507 next if ($count > 0);
508
509 # dump host and share header for -q
510 if ($opt{q}) {
511 if ($host_header) {
512 print $host_header;
513 $host_header = undef;
514 }
515 print $share_header;
516 }
517
518 # dump some log
519 print curr_time," ", $share;
520
521 $sth->{insert_backups}->execute(
522 $hostID,
523 $backupNum,
524 $backup->{'endTime'},
525 substr($backup->{'type'},0,4),
526 $shareID,
527 );
528
529 my ($f, $nf, $d, $nd, $size) = recurseDir($bpc, $hostname, $files, $backupNum, $share, "", $shareID);
530
531 eval {
532 $sth->{update_backups_size}->execute(
533 $size,
534 $hostID,
535 $backupNum,
536 $backup->{'endTime'},
537 substr($backup->{'type'},0,4),
538 $shareID,
539 );
540 print " commit";
541 $dbh->commit();
542 };
543 if ($@) {
544 print " rollback";
545 $dbh->rollback();
546 }
547
548 my $dur = (time() - $t) || 1;
549 printf(" %d/%d files %d/%d dirs %0.2f MB [%.2f/s dur: %s]\n",
550 $nf, $f, $nd, $d,
551 ($size / 1024 / 1024),
552 ( ($f+$d) / $dur ),
553 fmt_time($dur)
554 );
555
556 hest_update($hostID, $shareID, $backupNum) if ($nf + $nd > 0);
557 }
558
559 }
560 }
561 undef $sth;
562 $dbh->commit();
563 $dbh->disconnect();
564
565 print "total duration: ",fmt_time(time() - $start_t),"\n";
566
567 $pidfile->remove;
568
569 sub getShareID() {
570
571 my ($share, $hostID, $hostname) = @_;
572
573 $sth->{share_id} ||= $dbh->prepare(qq{
574 SELECT ID FROM shares WHERE hostID=? AND name=?
575 });
576
577 $sth->{share_id}->execute($hostID,$share);
578
579 my ($id) = $sth->{share_id}->fetchrow_array();
580
581 return $id if (defined($id));
582
583 $sth->{insert_share} ||= $dbh->prepare(qq{
584 INSERT INTO shares
585 (hostID,name,share)
586 VALUES (?,?,?)
587 });
588
589 my $drop_down = $hostname . '/' . $share;
590 $drop_down =~ s#//+#/#g;
591
592 $sth->{insert_share}->execute($hostID,$share, $drop_down);
593 return $dbh->last_insert_id(undef,undef,'shares',undef);
594 }
595
596 sub found_in_db {
597
598 my @data = @_;
599 shift @data;
600
601 my ($key, $shareID,undef,$name,$path,$date,undef,$size) = @_;
602
603 return $beenThere->{$key} if (defined($beenThere->{$key}));
604
605 $sth->{file_in_db} ||= $dbh->prepare(qq{
606 SELECT 1 FROM files
607 WHERE shareID = ? and
608 path = ? and
609 size = ? and
610 ( date = ? or date = ? or date = ? )
611 LIMIT 1
612 });
613
614 my @param = ($shareID,$path,$size,$date, $date-$dst_offset, $date+$dst_offset);
615 $sth->{file_in_db}->execute(@param);
616 my $rows = $sth->{file_in_db}->rows;
617 print STDERR "## found_in_db($shareID,$path,$date,$size) ",( $rows ? '+' : '-' ), join(" ",@param), "\n" if ($debug >= 3);
618
619 $beenThere->{$key}++;
620
621 $sth->{'insert_files'}->execute(@data) unless ($rows);
622 return $rows;
623 }
624
625 ####################################################
626 # recursing through filesystem structure and #
627 # and returning flattened files list #
628 ####################################################
629 sub recurseDir($$$$$$$$) {
630
631 my ($bpc, $hostname, $files, $backupNum, $share, $dir, $shareID) = @_;
632
633 print STDERR "\nrecurse($hostname,$backupNum,$share,$dir,$shareID)\n" if ($debug >= 1);
634
635 my ($nr_files, $new_files, $nr_dirs, $new_dirs, $size) = (0,0,0,0,0);
636
637 { # scope
638 my @stack;
639
640 print STDERR "# dirAttrib($backupNum, $share, $dir)\n" if ($debug >= 2);
641 my $filesInBackup = $files->dirAttrib($backupNum, $share, $dir);
642
643 # first, add all the entries in current directory
644 foreach my $path_key (keys %{$filesInBackup}) {
645 print STDERR "# file ",Dumper($filesInBackup->{$path_key}),"\n" if ($debug >= 3);
646 my @data = (
647 $shareID,
648 $backupNum,
649 $path_key,
650 $filesInBackup->{$path_key}->{'relPath'},
651 $filesInBackup->{$path_key}->{'mtime'},
652 $filesInBackup->{$path_key}->{'type'},
653 $filesInBackup->{$path_key}->{'size'}
654 );
655
656 my $key = join(" ", (
657 $shareID,
658 $dir,
659 $path_key,
660 $filesInBackup->{$path_key}->{'mtime'},
661 $filesInBackup->{$path_key}->{'size'}
662 ));
663
664 my $key_dst_prev = join(" ", (
665 $shareID,
666 $dir,
667 $path_key,
668 $filesInBackup->{$path_key}->{'mtime'} - $dst_offset,
669 $filesInBackup->{$path_key}->{'size'}
670 ));
671
672 my $key_dst_next = join(" ", (
673 $shareID,
674 $dir,
675 $path_key,
676 $filesInBackup->{$path_key}->{'mtime'} + $dst_offset,
677 $filesInBackup->{$path_key}->{'size'}
678 ));
679
680 my $found;
681 if (
682 ! defined($beenThere->{$key}) &&
683 ! defined($beenThere->{$key_dst_prev}) &&
684 ! defined($beenThere->{$key_dst_next}) &&
685 ! ($found = found_in_db($key, @data))
686 ) {
687 print STDERR "# key: $key [", $beenThere->{$key},"]" if ($debug >= 2);
688
689 if ($filesInBackup->{$path_key}->{'type'} == BPC_FTYPE_DIR) {
690 $new_dirs++ unless ($found);
691 print STDERR " dir\n" if ($debug >= 2);
692 } else {
693 $new_files++ unless ($found);
694 print STDERR " file\n" if ($debug >= 2);
695 }
696 $size += $filesInBackup->{$path_key}->{'size'} || 0;
697 }
698
699 if ($filesInBackup->{$path_key}->{'type'} == BPC_FTYPE_DIR) {
700 $nr_dirs++;
701
702 my $full_path = $dir . '/' . $path_key;
703 push @stack, $full_path;
704 print STDERR "### store to stack: $full_path\n" if ($debug >= 3);
705
706 # my ($f,$nf,$d,$nd) = recurseDir($bpc, $hostname, $backups, $backupNum, $share, $path_key, $shareID) unless ($beenThere->{$key});
707 #
708 # $nr_files += $f;
709 # $new_files += $nf;
710 # $nr_dirs += $d;
711 # $new_dirs += $nd;
712
713 } else {
714 $nr_files++;
715 }
716 }
717
718 print STDERR "## STACK ",join(", ", @stack),"\n" if ($debug >= 2);
719
720 while ( my $dir = shift @stack ) {
721 my ($f,$nf,$d,$nd, $s) = recurseDir($bpc, $hostname, $files, $backupNum, $share, $dir, $shareID);
722 print STDERR "# $dir f: $f nf: $nf d: $d nd: $nd\n" if ($debug >= 1);
723 $nr_files += $f;
724 $new_files += $nf;
725 $nr_dirs += $d;
726 $new_dirs += $nd;
727 $size += $s;
728 }
729 }
730
731 return ($nr_files, $new_files, $nr_dirs, $new_dirs, $size);
732 }
733

Properties

Name Value
svn:executable *

  ViewVC Help
Powered by ViewVC 1.1.26