File Coverage

blib/lib/App/MBUtiny/Collector.pm
Criterion Covered Total %
statement 88 195 45.1
branch 16 86 18.6
condition 9 41 21.9
subroutine 17 21 80.9
pod 9 9 100.0
total 139 352 39.4


line stmt bran cond sub pod time code
1             package App::MBUtiny::Collector; # $Id: Collector.pm 132 2019-07-19 14:28:28Z abalama $
2 2     2   57206 use strict;
  2         11  
  2         64  
3 2     2   559 use utf8;
  2         16  
  2         9  
4              
5             =encoding utf-8
6              
7             =head1 NAME
8              
9             App::MBUtiny::Collector - Collector class
10              
11             =head1 VIRSION
12              
13             Version 1.03
14              
15             =head1 SYNOPSIS
16              
17             use App::MBUtiny::Collector;
18              
19             my $collector_config = [
20             {
21             url => 'https://user:pass@example.com/mbutiny',
22             comment => 'Remote collector said blah-blah-blah',
23             timeout => 180
24             },
25             {
26             comment => 'Local collector said blah-blah-blah',
27             },
28             # ...
29             ];
30              
31             my $collector = new App::MBUtiny::Collector(
32             collector_config => $collector_config,
33             dbi => $dbi, # App::MBUtiny::Collector::DBI object
34             );
35              
36             my $colret = $collector->check;
37              
38             print STDERR $collector->error if $collector->error;
39              
40             =head1 DESCRIPTION
41              
42             Collector class
43              
44             =head2 new
45              
46             my $collector = new App::MBUtiny::Collector(
47             collector_config => [{}, {}, ...],
48             dbi_config => {...}, # App::MBUtiny::Collector::DBI arguments
49             dbi => $dbi, # App::MBUtiny::Collector::DBI object
50             );
51              
52             Creates the collector object with local database supporting
53              
54             =over 4
55              
56             =item B
57              
58             collector_config => [
59             {
60             url => 'https://user:pass@example.com/mbutiny',
61             comment => 'Remote collector said blah-blah-blah',
62             timeout => 180
63             },
64             {
65             comment => 'Local collector said blah-blah-blah',
66             },
67             # ...
68             ],
69              
70             Array of attributes for initializing specified collectors
71              
72             =item B
73              
74             dbi => new App::MBUtiny::Collector::DBI(...),
75              
76             Sets pre-initialized L object
77              
78             =item B
79              
80             dbi_config => {...},
81              
82             Hash of L arguments
83              
84             =back
85              
86             =head2 check
87              
88             my @collector_ids = $collector->check;
89             my $collector_ids = $collector->check; # text notation
90              
91             Checks clist of available collectors and returns list of
92             checked collectors as URLs or DSNs
93              
94             See also L method
95              
96             =head2 collectors
97              
98             my @collector_list = $collector->collectora;
99              
100             Returns list of initialized collectors
101              
102             =head2 dbi
103              
104             my $dbi = $collector->dbi;
105              
106             Returns DBI object of local database (local collector)
107              
108             =head2 error
109              
110             print $collector->error("Foo"); # Foo
111             print $collector->error("Bar"); # Foo\nBar
112             print $collector->error; # Foo\nBar
113             print $collector->error(""); # <"">
114              
115             Sets and gets the error pool
116              
117             =head2 fixup
118              
119             my @collector_ids = $collector->fixup(
120             operation => "del",
121             name => "foo",
122             file => "foo-2019-06-25.tar.gz",
123             );
124              
125             Fixation of the "del" operation on current storage
126              
127             my @collector_ids = $collector->fixup(
128             operation => "put",
129             name => "foo",
130             file => "foo-2019-06-25.tar.gz",
131             size => 123453,
132             md5 => "...",
133             sha1 => "...",
134             status => 1,
135             error => "...",
136             comment => "...",
137             );
138              
139             Fixation of the "put" operation on current storage
140              
141             =over 4
142              
143             =item B
144              
145             Comment of the "put" operation
146              
147             Scope: put
148              
149             =item B
150              
151             Error message of the performed operation
152              
153             Scope: put
154              
155             =item B
156              
157             Name of backup file. Required argument
158              
159             Scope: put, del
160              
161             =item B, B
162              
163             MD5 and SHA1 checksums of backup file
164              
165             Scope: put
166              
167             =item B
168              
169             Name of backup. Required argument
170              
171             Scope: put, del
172              
173             =item B
174              
175             Name of operation: del/put
176              
177             Default: put
178              
179             =item B
180              
181             Size of backup file
182              
183             Scope: put
184              
185             =item B
186              
187             Status of backup operation: 0 or 1
188              
189             Default: 0 (operation failed)
190              
191             Scope: put
192              
193             =back
194              
195             =head2 info
196              
197             my %info = $collector->info(
198             name => "foo",
199             file => "foo-2019-06-25.tar.gz",
200             );
201              
202             Gets information about specified file name
203              
204             Returns hash of values in "AS IN DATABASE DEFINED" format,
205             see L
206              
207             =head2 report
208              
209             my @last_backup_files = $collector->report( start => 123456789 );
210              
211             Returns list of last backups from all collectors as array of info-hashes.
212              
213             See L method
214              
215             =head1 PUBLIC FUNCTIONS
216              
217             =head2 int2type
218              
219             my $type = int2type(0); # internal
220              
221             Returns name of specified type
222              
223             NOTE: This variable NOT imported automatically
224              
225             =head1 VARIABLES
226              
227             =head2 COLLECTOR_TYPES
228              
229             Returns hash-structure ("type_name" => int_value) of available collector types
230              
231             NOTE: This variable imported automatically
232              
233             =head1 HISTORY
234              
235             See C file
236              
237             =head1 TO DO
238              
239             See C file
240              
241             =head1 SEE ALSO
242              
243             L
244              
245             =head1 AUTHOR
246              
247             Serż Minus (Sergey Lepenkov) L Eabalama@cpan.orgE
248              
249             =head1 COPYRIGHT
250              
251             Copyright (C) 1998-2019 D&D Corporation. All Rights Reserved
252              
253             =head1 LICENSE
254              
255             This program is free software; you can redistribute it and/or
256             modify it under the same terms as Perl itself.
257              
258             See C file and L
259              
260             =cut
261              
262 2     2   112 use vars qw/ $VERSION @EXPORT @EXPORT_OK /;
  2         4  
  2         131  
263             $VERSION = '1.03';
264              
265 2     2   13 use Carp;
  2         4  
  2         122  
266 2     2   330 use CTK::ConfGenUtil;
  2         987  
  2         131  
267 2     2   367 use CTK::TFVals qw/ :ALL /;
  2         1662  
  2         384  
268 2     2   740 use App::MBUtiny::Collector::DBI;
  2         4  
  2         92  
269 2     2   893 use App::MBUtiny::Collector::Client;
  2         7  
  2         61  
270 2     2   540 use App::MBUtiny::Util qw/hide_password/;
  2         6  
  2         128  
271              
272             use constant {
273 2         97 COLLECTOR_TYPES => {
274             internal => 0,
275             external => 1,
276             },
277 2     2   13 };
  2         4  
278              
279 2     2   11 use base qw/Exporter/;
  2         4  
  2         4335  
280             @EXPORT_OK = qw/
281             int2type
282             /;
283             @EXPORT = qw/
284             COLLECTOR_TYPES
285             /;
286              
287             sub new {
288 1     1 1 2 my $class = shift;
289 1         4 my %args = @_;
290 1   50     4 my $collector_config = $args{collector_config} || [];
291 1   50     5 my $dbi_config = $args{dbi_config} || {};
292 1 50       11 croak("Incorrect collector config. Array expected!") unless is_array($collector_config);
293 1   33     12 my $dbi = $args{dbi} || App::MBUtiny::Collector::DBI->new(%$dbi_config);
294 1 50 33     7 croak("Can't use incorrect dbi object") unless $dbi && ref($dbi) eq 'App::MBUtiny::Collector::DBI';
295              
296             # Collectors
297 1         3 my @collectors = ();
298 1         2 my $internal = 0;
299 1         3 foreach my $cltr (@$collector_config) {
300 1         4 my $url = value($cltr, "url");
301 1 50       51 if ($url) {
302 0         0 push @collectors, {
303             type => "external",
304             url => $url,
305             comment => uv2null(value($cltr, "comment")),
306             timeout => uv2zero(value($cltr, "timeout")),
307             };
308             } else {
309 1 50       8 push @collectors, {
310             type => "internal",
311             comment => uv2null(value($cltr, "comment")),
312             } unless $internal;
313 1         51 $internal++;
314             }
315             }
316 1 50       3 unless (@collectors) {
317 0 0       0 push @collectors, {
318             type => "internal",
319             comment => "",
320             } unless $internal;
321             }
322              
323 1         6 my $self = bless {
324             collectors => [@collectors],
325             dbi => $dbi,
326             errors => [],
327             }, $class;
328              
329 1         4 return $self;
330             }
331             sub error {
332 1     1 1 8 my $cnt = @_;
333 1         2 my $self = shift;
334 1         2 my $s = shift;
335 1   50     7 my $errors = $self->{errors} || [];
336 1 50       4 if ($cnt >= 2) {
337 1 50       3 if ($s) {
338 0         0 push @$errors, $s;
339             } else {
340 1         2 $errors = [];
341             }
342 1         3 $self->{errors} = $errors;
343             }
344 1         2 return join("\n", @$errors);
345             }
346             sub dbi {
347 1     1 1 2 my $self = shift;
348 1         2 return $self->{dbi};
349             }
350             sub collectors {
351 2     2 1 3 my $self = shift;
352 2         3 my $collectors = $self->{collectors};
353 2 100       5 return scalar(@$collectors) unless wantarray;
354 1         4 return @$collectors;
355             }
356              
357             sub check {
358 0     0 1 0 my $self = shift;
359 0         0 $self->error("");
360 0         0 my $dbi = $self->dbi;
361 0 0       0 return "" unless $self->collectors;
362 0         0 my @ret = (); # List of DSN/URL_wo_password
363 0         0 foreach my $collector ($self->collectors) {
364 0         0 my $type = $collector->{type};
365 0 0       0 if ($type eq 'internal') { # Internal
366 0 0       0 if ($dbi->error) {
367 0         0 $self->error($dbi->error());
368 0         0 next;
369             };
370 0         0 push @ret, $dbi->dsn;
371             } else { # External
372 0   0     0 my $url = $collector->{url} || "";
373 0   0     0 my $timeout = $collector->{timeout} || 0;
374 0         0 my $client = new App::MBUtiny::Collector::Client(
375             url => $url,
376             timeout => $timeout,
377             );
378 0 0       0 unless ($client->status) {
379 0         0 $self->error(join("%s\n%s", $client->transaction, $client->error));
380 0         0 next;
381             }
382 0         0 my $check = $client->check;
383 0 0       0 unless ($client->status) {
384 0         0 $self->error(join("\n", $client->transaction, $client->error));
385 0         0 next;
386             }
387 0         0 push @ret, hide_password($url);
388             }
389             }
390 0 0       0 return @ret ? join("\n", @ret) : "";
391             }
392             sub fixup {
393 1     1 1 8 my $self = shift;
394 1         7 my %args = @_;
395 1         5 $self->error("");
396 1         3 my $dbi = $self->dbi;
397 1 50       4 return "" unless $self->collectors;
398 1         2 my @ret = (); # List of DSN/URL_wo_password
399              
400 1         3 foreach my $collector ($self->collectors) {
401 1         2 my $type = $collector->{type};
402 1   50     11 my $url = $collector->{url} || "";
403 1   50     13 my $timeout = $collector->{timeout} || 0;
404 1   50     5 my $comment = join("\n", grep {$_} ($args{comment}, $collector->{comment})) // "";
  2         6  
405 1   50     4 my $op = $args{operation} || '';
406 1 50       4 if ($op =~ /^(del)|(rem)/) { # Delete (op)
407 0 0       0 if ($type eq 'internal') { # Internal
408             $dbi->del(
409             type => _type2int($type),
410             name => $args{name},
411             file => $args{file},
412 0 0       0 ) or do {
413 0         0 $self->error($dbi->error());
414 0         0 next;
415             };
416 0         0 push @ret, $dbi->dsn;
417             } else { # External
418 0         0 my $client = new App::MBUtiny::Collector::Client( url => $url, timeout => $timeout );
419 0 0       0 unless ($client->status) {
420 0         0 $self->error(join("%s\n%s", $client->transaction, $client->error));
421 0         0 next;
422             }
423             $client->del(
424             type => _type2int($type),
425             name => $args{name},
426             file => $args{file},
427 0 0       0 ) or do {
428 0         0 $self->error(join("\n", $client->transaction, $client->error));
429 0         0 next;
430             };
431 0         0 push @ret, hide_password($url);
432             }
433             } else { # Put (op)
434 1 50       4 if ($type eq 'internal') { # Internal
435             $dbi->add(
436             type => _type2int($type),
437             name => $args{name},
438             file => $args{file},
439             size => $args{size},
440             md5 => $args{md5},
441             sha1 => $args{sha1},
442             status => $args{status},
443             error => $args{error},
444             comment => $comment,
445 1 50       3 ) or do {
446 0         0 $self->error($dbi->error());
447 0         0 next;
448             };
449 1         5 push @ret, $dbi->dsn;
450             } else { # External
451 0         0 my $client = new App::MBUtiny::Collector::Client( url => $url, timeout => $timeout );
452 0 0       0 unless ($client->status) {
453 0         0 $self->error(join("%s\n%s", $client->transaction, $client->error));
454 0         0 next;
455             }
456             $client->add(
457             type => _type2int($type),
458             name => $args{name},
459             file => $args{file},
460             size => $args{size},
461             md5 => $args{md5},
462             sha1 => $args{sha1},
463             status => $args{status},
464             error => $args{error},
465             comment => $comment,
466 0 0       0 ) or do {
467 0         0 $self->error(join("\n", $client->transaction, $client->error));
468 0         0 next;
469             };
470 0         0 push @ret, hide_password($url);
471             }
472             }
473             }
474              
475 1 50       9 return @ret ? join("\n", @ret) : "";
476             }
477             sub info {
478 0     0 1 0 my $self = shift;
479 0         0 my %args = @_;
480 0         0 $self->error("");
481 0         0 my $dbi = $self->dbi;
482 0 0       0 return () unless $self->collectors;
483 0         0 my %info = (); # Information about file
484 0         0 foreach my $collector ($self->collectors) {
485 0 0       0 if ($collector->{type} eq 'internal') { # Internal
486             %info = $dbi->get(
487             name => $args{name},
488             file => $args{file},
489 0 0       0 ) or do {
490 0         0 $self->error($dbi->error());
491 0         0 next;
492             };
493             } else { # External
494             my $client = new App::MBUtiny::Collector::Client(
495             url => $collector->{url} || "",
496 0   0     0 timeout => $collector->{timeout} || 0,
      0        
497             );
498 0 0       0 unless ($client->status) {
499 0         0 $self->error(join("%s\n%s", $client->transaction, $client->error));
500 0         0 next;
501             }
502             %info = $client->get(
503             name => $args{name},
504             file => $args{file},
505 0         0 );
506 0 0       0 if ($client->error) {
507 0         0 $self->error(join("\n", $client->transaction, $client->error));
508 0         0 next;
509             }
510             }
511 0 0 0     0 last if $info{id} && $info{status};
512             }
513 0         0 return %info;
514             }
515             sub report {
516 0     0 1 0 my $self = shift;
517 0         0 my %args = @_;
518 0         0 $self->error("");
519 0         0 my $dbi = $self->dbi;
520 0 0       0 return () unless $self->collectors;
521 0         0 my %mreport = (); # Report
522 0         0 foreach my $collector ($self->collectors) {
523 0         0 my @rep = ();
524 0 0       0 if ($collector->{type} eq 'internal') { # Internal
525 0         0 @rep = $dbi->report( start => $args{start});
526 0 0       0 if ($dbi->error()) {
527 0         0 $self->error($dbi->error());
528 0         0 next;
529             };
530             } else { # External
531             my $client = new App::MBUtiny::Collector::Client(
532             url => $collector->{url} || "",
533 0   0     0 timeout => $collector->{timeout} || 0,
      0        
534             );
535 0 0       0 if ($client->error) {
536 0         0 $self->error(join("%s\n%s", $client->transaction, $client->error));
537 0         0 next;
538             }
539 0         0 @rep = $client->report( start => $args{start});
540 0 0       0 if ($client->error) {
541 0         0 $self->error(join("\n", $client->transaction, $client->error));
542 0         0 next;
543             };
544             }
545              
546             # Select here!
547 0         0 foreach my $rec (@rep) {
548 0   0     0 my $k = sprintf("%s-t%d-s%d", $rec->{name} || 'noname', $rec->{type} || 0, $rec->{status} || 0);
      0        
      0        
549 0         0 my $t = $mreport{$k};
550 0 0       0 unless ($t) {
551 0         0 $mreport{$k} = $rec;
552 0         0 next;
553             }
554 0 0       0 $mreport{$k} = $rec if $t->{'time'} < $rec->{'time'};
555             }
556             }
557 0         0 return sort {$a->{'time'} <=> $b->{'time'}} values %mreport;
  0         0  
558             }
559             sub int2type {
560 0     0 1 0 my $s = shift;
561 0 0       0 $s = 0 unless $s;
562 0         0 my %types = %{(COLLECTOR_TYPES())};
  0         0  
563 0         0 my %inv = reverse %types;
564 0 0       0 $inv{$s} || $inv{0};
565             }
566             sub _type2int {
567 1     1   2 my $s = shift;
568 1 50       9 return 0 unless $s;
569 1         2 my %types = %{(COLLECTOR_TYPES())};
  1         6  
570 1 50       11 $types{$s} || 0;
571             }
572              
573             1;
574              
575             __END__