/[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 136 - (show annotations)
Fri Sep 23 15:04:37 2005 UTC (18 years, 7 months ago) by dpavlin
File size: 16539 byte(s)
make backup size bigint

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

Properties

Name Value
svn:executable *

  ViewVC Help
Powered by ViewVC 1.1.26