]> git.sur5r.net Git - bacula/bacula/blob - gui/bweb/cgi/bgraph.pl
ebl Modify "missing job" view to take all kind of job
[bacula/bacula] / gui / bweb / cgi / bgraph.pl
1 #!/usr/bin/perl -w
2 use strict;
3
4 =head1 LICENSE
5
6    Bweb - A Bacula web interface
7    Bacula® - The Network Backup Solution
8
9    Copyright (C) 2000-2006 Free Software Foundation Europe e.V.
10
11    The main author of Bweb is Eric Bollengier.
12    The main author of Bacula is Kern Sibbald, with contributions from
13    many others, a complete list can be found in the file AUTHORS.
14
15    This program is Free Software; you can redistribute it and/or
16    modify it under the terms of version two of the GNU General Public
17    License as published by the Free Software Foundation plus additions
18    that are listed in the file LICENSE.
19
20    This program is distributed in the hope that it will be useful, but
21    WITHOUT ANY WARRANTY; without even the implied warranty of
22    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23    General Public License for more details.
24
25    You should have received a copy of the GNU General Public License
26    along with this program; if not, write to the Free Software
27    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
28    02110-1301, USA.
29
30    Bacula® is a registered trademark of John Walker.
31    The licensor of Bacula is the Free Software Foundation Europe
32    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zurich,
33    Switzerland, email:ftf@fsfeurope.org.
34
35 =head1 VERSION
36
37     $Id$
38
39 =cut
40
41 use Bweb;
42
43 use Data::Dumper;
44 use CGI;
45
46 use POSIX qw/strftime/;
47 use File::Basename qw/basename dirname/;
48
49 my $conf = new Bweb::Config(config_file => $Bweb::config_file);
50 $conf->load();
51 my $bweb = new Bweb(info => $conf);
52 $bweb->connect_db();
53 my $dbh = $bweb->{dbh};
54 my $debug = $bweb->{debug};
55
56 # Job table keep use Media or Job retention, so it's quite enought
57 # for good statistics
58 # CREATE TABLE job_old (LIKE Job);
59 # INSERT INTO job_old
60 #    (SELECT * FROM Job WHERE JobId NOT IN (SELECT JobId FROM job_old) );
61 my $jobt = $conf->{stat_job_table} || 'Job';
62
63 my $graph = CGI::param('graph') || 'job_size';
64 my $legend = CGI::param('legend') || 'on' ;
65 $legend = ($legend eq 'on')?1:0;
66
67 my $arg = $bweb->get_form(qw/width height limit offset age where jobid
68                              jfilesets level status jjobnames jclients jclient_groups/);
69
70 my ($limitq, $label) = $bweb->get_limit(age   => $arg->{age},
71                                         limit => $arg->{limit},
72                                         offset=> $arg->{offset},
73                                         order => "$jobt.StartTime ASC",
74                                         );
75
76 my $statusq='';
77 if ($arg->{status} and $arg->{status} ne 'Any') {
78     $statusq = " AND $jobt.JobStatus = '$arg->{status}' ";
79 }
80     
81 my $levelq='';
82 if ($arg->{level} and $arg->{level} ne 'Any') {
83     $levelq = " AND $jobt.Level = '$arg->{level}' ";
84
85
86 my $filesetq='';
87 if ($arg->{jfilesets}) {
88     $filesetq = " AND FileSet.FileSet IN ($arg->{qfilesets}) ";
89
90
91 my $jobnameq='';
92 if ($arg->{jjobnames}) {
93     $jobnameq = " AND $jobt.Name IN ($arg->{jjobnames}) ";
94 } else {
95     $arg->{jjobnames} = 'all';  # skip warning
96
97
98 my $clientq='';
99 if ($arg->{jclients}) {
100     $clientq = " AND Client.Name IN ($arg->{jclients}) ";
101 } else {
102     $arg->{jclients} = 'all';   # skip warning
103 }
104
105 my $groupf='';                  # from clause
106 my $groupq='';                  # whre clause
107 if ($arg->{jclient_groups}) {
108     $groupf = " JOIN client_group_member ON (Client.ClientId = client_group_member.clientid) 
109                 JOIN client_group USING (client_group_id)";
110     $groupq = " AND client_group_name IN ($arg->{jclient_groups}) ";
111 }
112
113 $bweb->can_do('r_view_stat');
114 my $filter = $bweb->get_client_filter();
115
116 my $gtype = CGI::param('gtype') || 'bars';
117
118
119 # in this mode, we generate an image and an imagemap
120 if ($gtype eq 'balloon') {
121     use Digest::MD5 qw(md5_hex);
122     use GBalloon;
123
124     my $b = new GBalloon(width=>$arg->{width}, 
125                          height =>$arg->{height});
126
127     my $order;
128     my %legend = (x_title => 'Time', 
129                   x_func => sub { 
130                       POSIX::strftime('%H:%M', gmtime($_[0])) 
131                       }
132                   ) ;
133     if ($graph eq 'job_time_size') {
134         $order = 'JobFiles,JobBytes';
135
136         $legend{y_title} = 'Nb files';
137         $legend{y_func} = sub { int(shift)};
138         $legend{z_title} = 'Size';
139         $legend{z_func} = \&Bweb::human_size;
140     } else {
141         $order = 'JobBytes,JobFiles';
142
143         $legend{y_title} = 'Size';
144         $legend{y_func} = \&Bweb::human_size;
145         $legend{z_title} = 'Nb files';
146         $legend{z_func} = sub { int(shift)};
147     }
148
149     $b->set_legend_axis(%legend);
150
151     my $all = $dbh->selectall_arrayref("
152 SELECT $bweb->{sql}->{SEC_TO_INT}(  $bweb->{sql}->{UNIX_TIMESTAMP}(EndTime)
153                                   - $bweb->{sql}->{UNIX_TIMESTAMP}(StartTime))
154          AS duration, $order, JobId, $jobt.Name
155        
156  FROM $jobt, Client $filter $groupf
157 WHERE $jobt.ClientId = Client.ClientId
158   AND $jobt.Type = 'B'
159   $clientq
160   $statusq
161   $levelq
162   $jobnameq
163   $groupq
164 $limitq
165 ");
166
167     foreach my $a (@$all) {
168         $b->add_point($a->[0], $a->[1], $a->[2], 
169                       "?action=job_zoom;jobid=$a->[3]",
170                       "$a->[4] $legend{z_title} " . $legend{z_func}($a->[2]));
171     }
172     
173     $b->init_gd();
174     $b->finalize();
175
176     my $md5_rep = md5_hex(join(":", map { $arg->{$_} } sort keys %$arg));
177
178     # need to cleanup this path
179     open(FP, ">$conf->{fv_write_path}/$md5_rep.png");
180     print FP $GBalloon::gd->png;
181     close(FP);
182     
183     print $b->get_imagemap("Job overview", "/bweb/fv/$md5_rep.png");
184
185     exit 0;
186 }
187
188 print CGI::header('image/png');
189
190 sub get_graph
191 {
192     my (@options) = @_;
193     my $graph;
194     if ($gtype eq 'lines') {
195         use GD::Graph::lines;
196         $graph = GD::Graph::lines->new ( $arg->{width}, $arg->{height} );
197
198     } elsif ($gtype eq 'bars') {
199         use GD::Graph::bars;
200         $graph = GD::Graph::bars->new ( $arg->{width}, $arg->{height} );
201
202     } elsif ($gtype eq 'linespoints') {
203         use GD::Graph::linespoints;
204         $graph = GD::Graph::linespoints->new ( $arg->{width}, $arg->{height} );
205
206 #   this doesnt works at this time
207 #    } elsif ($gtype eq 'bars3d') {
208 #       use GD::Graph::bars3d;
209 #       $graph = GD::Graph::bars3d->new ( $arg->{width}, $arg->{height} );
210
211     } else {
212         return undef;
213     }
214
215     $graph->set('x_label' => 'Time',
216                 'x_number_format' => sub { strftime('%D', localtime($_[0])) },
217                 'x_tick_number' => 5*$arg->{width}/800,
218                 @options,
219                 );
220
221     return $graph;
222 }
223
224 sub make_tab
225 {
226     my ($all_row) = @_;
227
228     my $i=0;
229     my $last_date=0;
230
231     my $ret = {};
232     
233     foreach my $row (@$all_row) {
234         my $label = $row->[1] . "/" . $row->[2] ; # client/backup name
235
236         $ret->{date}->[$i]   = $row->[0];       
237         $ret->{$label}->[$i] = $row->[3];
238         $i++;
239         $last_date = $row->[0];
240     }
241
242     # insert a fake element
243     foreach my $elt ( keys %{$ret}) {
244         $ret->{$elt}->[$i] =  undef;
245     }
246
247     $ret->{date}->[$i] = $last_date + 1;
248
249     my $date = $ret->{date} ;
250     delete $ret->{date};
251
252     return ($date, $ret);
253 }
254
255 sub make_tab_sum
256 {
257     my ($all_row) = @_;
258
259     my $i=0;
260     my $last_date=0;
261
262     my $ret = {};
263     
264     foreach my $row (@$all_row) {
265         $ret->{date}->[$i]   = $row->[0];       
266         $ret->{nb}->[$i] = $row->[1];
267         $i++;
268     }
269
270     return ($ret);
271 }
272
273 if ($graph eq 'job_size') {
274
275     my $query = "
276 SELECT 
277        UNIX_TIMESTAMP($jobt.StartTime)  AS starttime,
278        Client.Name                      AS clientname,
279        $jobt.Name                       AS jobname,
280        $jobt.JobBytes                   AS jobbytes
281 FROM $jobt, FileSet, Client $filter $groupf
282 WHERE $jobt.ClientId = Client.ClientId
283   AND $jobt.FileSetId = FileSet.FileSetId
284   AND $jobt.Type = 'B'
285   $clientq
286   $statusq
287   $filesetq
288   $levelq
289   $jobnameq
290   $groupq
291 $limitq
292 ";
293
294     print STDERR $query if ($debug);
295
296     my $obj = get_graph('title' => "Job Size : $arg->{jclients}/$arg->{jjobnames}",
297                         'y_label' => 'Size',
298                         'y_min_value' => 0,
299                         'y_number_format' => \&Bweb::human_size,
300                         );
301
302     my $all = $dbh->selectall_arrayref($query) ;
303
304     my ($d, $ret) = make_tab($all);
305     if ($legend) {
306         $obj->set_legend(keys %$ret);
307     }
308     print $obj->plot([$d, values %$ret])->png;
309 }
310
311 if ($graph eq 'job_file') {
312
313     my $query = "
314 SELECT 
315        UNIX_TIMESTAMP($jobt.StartTime)  AS starttime,
316        Client.Name                      AS clientname,
317        $jobt.Name                       AS jobname,
318        $jobt.JobFiles                   AS jobfiles
319 FROM $jobt, FileSet, Client $filter $groupf
320 WHERE $jobt.ClientId = Client.ClientId
321   AND $jobt.FileSetId = FileSet.FileSetId
322   AND $jobt.Type = 'B'
323   $clientq
324   $statusq
325   $filesetq
326   $levelq
327   $jobnameq
328   $groupq
329 $limitq
330 ";
331
332     print STDERR $query if ($debug);
333
334     my $obj = get_graph('title' => "Job Files : $arg->{jclients}/$arg->{jjobnames}",
335                         'y_label' => 'Number Files',
336                         'y_min_value' => 0,
337                         );
338
339     my $all = $dbh->selectall_arrayref($query) ;
340
341     my ($d, $ret) = make_tab($all);
342     if ($legend) {
343         $obj->set_legend(keys %$ret);
344     }
345     print $obj->plot([$d, values %$ret])->png;
346 }
347
348 # it works only with postgresql at this time
349 # we dont use $jobt because we use File, so job is in Job table
350 elsif ($graph eq 'file_histo' and $arg->{where}) {
351     
352     my $dir  = $dbh->quote(dirname($arg->{where}) . '/');
353     my $file = $dbh->quote(basename($arg->{where}));
354
355     my $query = "
356 SELECT UNIX_TIMESTAMP(Job.StartTime)    AS starttime,
357        Client.Name                      AS client,
358        Job.Name                         AS jobname,
359        base64_decode_lstat(8,LStat)     AS lstat
360
361 FROM Job, FileSet, Filename, Path, File, Client $filter
362 WHERE Job.ClientId = Client.ClientId
363   AND Job.FileSetId = FileSet.FileSetId
364   AND Job.Type = 'B'
365   AND File.JobId = Job.JobId
366   AND File.FilenameId = Filename.FilenameId
367   AND File.PathId = Path.PathId
368   AND Path.Path = $dir
369   AND Filename.Name = $file
370   $clientq
371   $statusq
372   $filesetq
373   $levelq
374   $jobnameq
375 $limitq
376 ";
377
378     print STDERR $query if ($debug);
379
380     my $all = $dbh->selectall_arrayref($query) ;
381
382     my $obj = get_graph('title' => "File size : $arg->{where}",
383                         'y_label' => 'File size',
384                         'y_min_value' => 0,
385                         'y_min_value' => 0,
386                         'y_number_format' => \&Bweb::human_size,
387                         );
388
389
390     my ($d, $ret) = make_tab($all);
391     if ($legend) {
392         $obj->set_legend(keys %$ret);
393     }
394     print $obj->plot([$d, values %$ret])->png;
395 }
396
397 # it works only with postgresql at this time
398 # TODO: use brestore_missing_path
399 elsif ($graph eq 'rep_histo' and $arg->{where}) {
400     
401     my $dir  = $arg->{where};
402     $dir .= '/' if ($dir !~ m!/$!);
403     $dir = $dbh->quote($dir);
404
405     my $query = "
406 SELECT UNIX_TIMESTAMP(Job.StartTime) AS starttime,
407        Client.Name                   AS client,
408        Job.Name                      AS jobname,
409        brestore_pathvisibility.size  AS size
410
411 FROM Job, Client $filter, FileSet, Path, brestore_pathvisibility
412 WHERE Job.ClientId = Client.ClientId
413   AND Job.FileSetId = FileSet.FileSetId
414   AND Job.Type = 'B'
415   AND Job.JobId = brestore_pathvisibility.JobId
416   AND Path.PathId = brestore_pathvisibility.PathId
417   AND Path.Path = $dir
418   $clientq
419   $statusq
420   $filesetq
421   $levelq
422   $jobnameq
423 $limitq
424 ";
425
426     print STDERR $query if ($debug);
427
428     my $all = $dbh->selectall_arrayref($query) ;
429
430     my $obj = get_graph('title' => "Directory size : $arg->{where}",
431                         'y_label' => 'Directory size',
432                         'y_min_value' => 0,
433                         'y_min_value' => 0,
434                         'y_number_format' => \&Bweb::human_size,
435                         );
436
437
438     my ($d, $ret) = make_tab($all);
439     if ($legend) {
440         $obj->set_legend(keys %$ret);
441     }
442     print $obj->plot([$d, values %$ret])->png;
443 }
444
445 elsif ($graph eq 'job_rate') {
446
447     my $query = "
448 SELECT 
449        UNIX_TIMESTAMP($jobt.StartTime)  AS starttime,
450        Client.Name                      AS clientname,
451        $jobt.Name                       AS jobname,
452        $jobt.JobBytes /
453        ($bweb->{sql}->{SEC_TO_INT}(
454                           $bweb->{sql}->{UNIX_TIMESTAMP}(EndTime)  
455                         - $bweb->{sql}->{UNIX_TIMESTAMP}(StartTime)) + 0.01) 
456          AS rate
457
458 FROM $jobt, FileSet, Client $filter $groupf
459 WHERE $jobt.ClientId = Client.ClientId
460   AND $jobt.FileSetId = FileSet.FileSetId
461   AND $jobt.Type = 'B'
462   $clientq
463   $statusq
464   $filesetq
465   $levelq
466   $jobnameq
467   $groupq
468 $limitq
469 ";
470
471     print STDERR $query if ($debug);
472
473     my $obj = get_graph('title' => "Job Rate : $arg->{jclients}/$arg->{jjobnames}",
474                         'y_label' => 'Rate b/s',
475                         'y_min_value' => 0,
476                         'y_number_format' => \&Bweb::human_size,
477                         );
478
479     my $all = $dbh->selectall_arrayref($query) ;
480
481     my ($d, $ret) = make_tab($all);    
482     if ($legend) {
483         $obj->set_legend(keys %$ret);
484     }
485     print $obj->plot([$d, values %$ret])->png;
486 }
487
488
489
490 elsif ($graph eq 'job_duration') {
491
492     my $query = "
493 SELECT 
494        UNIX_TIMESTAMP($jobt.StartTime)                         AS starttime,
495        Client.Name                                             AS clientname,
496        $jobt.Name                                              AS jobname,
497   $bweb->{sql}->{SEC_TO_INT}(  $bweb->{sql}->{UNIX_TIMESTAMP}(EndTime)  
498                              - $bweb->{sql}->{UNIX_TIMESTAMP}(StartTime)) 
499          AS duration
500 FROM $jobt, FileSet, Client $filter $groupf
501 WHERE $jobt.ClientId = Client.ClientId
502   AND $jobt.FileSetId = FileSet.FileSetId
503   AND $jobt.Type = 'B'
504   $clientq
505   $statusq
506   $filesetq
507   $levelq
508   $jobnameq
509   $groupq
510 $limitq
511 ";
512
513     print STDERR $query if ($debug);
514
515     my $obj = get_graph('title' => "Job Duration : $arg->{jclients}/$arg->{jjobnames}",
516                         'y_label' => 'Duration',
517                         'y_min_value' => 0,
518                         'y_number_format' => \&Bweb::human_sec,
519                         );
520     my $all = $dbh->selectall_arrayref($query) ;
521
522     my ($d, $ret) = make_tab($all);
523     if ($legend) {
524         $obj->set_legend(keys %$ret);
525     }
526     print $obj->plot([$d, values %$ret])->png;
527
528
529 # number of job per day/hour
530 } elsif ($graph =~ /^job_(count|sum|avg)_((p?)(day|hour|month))$/) {
531     my $t = $1;
532     my $d = uc($2);
533     my $per_t = $3;
534     my ($limit, $label) = $bweb->get_limit(age   => $arg->{age},
535                                            limit => $arg->{limit},
536                                            offset=> $arg->{offset},
537                                            groupby => "A",
538                                            order => "A",
539                                            );
540     my @arg;                    # arg for plotting
541
542     if (!$per_t) {              # much better aspect
543         #$gtype = 'lines';
544     } else {
545         push @arg, ("x_number_format" => undef,
546                     "x_min_value" => 0,
547                     );
548     }
549
550     if ($t eq 'sum' or $t eq 'avg') {
551         push @arg, ('y_number_format' => \&Bweb::human_size);
552     }
553     
554     my $stime = $bweb->{sql}->{"STARTTIME_$d"};
555     $stime =~ s/Job\./$jobt\./;
556
557     my $query = "
558 SELECT
559      " . ($per_t?"":"UNIX_TIMESTAMP") . "($stime) AS A,
560      $t(JobBytes)                  AS nb
561 FROM $jobt, FileSet, Client $filter $groupf
562 WHERE $jobt.ClientId = Client.ClientId
563   AND $jobt.FileSetId = FileSet.FileSetId
564   AND $jobt.Type = 'B'
565   $clientq
566   $statusq
567   $filesetq
568   $levelq
569   $jobnameq
570   $groupq
571 $limit
572 ";
573
574     print STDERR $query  if ($debug);
575
576     my $obj = get_graph('title' => "Job $t : $arg->{jclients}/$arg->{jjobnames}",
577                         'y_label' => $t,
578                         'y_min_value' => 0,
579                         @arg,
580                         );
581
582     my $all = $dbh->selectall_arrayref($query) ;
583 #    print STDERR Data::Dumper::Dumper($all);
584     my ($ret) = make_tab_sum($all);
585
586     print $obj->plot([$ret->{date}, $ret->{nb}])->png;    
587
588
589
590 $dbh->disconnect();