]> git.sur5r.net Git - bacula/bacula/blob - gui/bweb/cgi/bgraph.pl
in bweb, fix warning
[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 Kern Sibbald.
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 JobHistory (LIKE Job);
59 # INSERT INTO JobHistory
60 #    (SELECT * FROM Job WHERE JobId NOT IN (SELECT JobId FROM JobHistory) );
61 my $jobt = $bweb->get_stat_table();
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 => "Job.StartTime ASC",
74                                         );
75
76 my $statusq='';
77 if ($arg->{status} and $arg->{status} ne 'Any') {
78     $statusq = " AND Job.JobStatus = '$arg->{status}' ";
79 }
80     
81 my $levelq='';
82 if ($arg->{level} and $arg->{level} !~ 'All|Any') {
83     $levelq = " AND Job.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 Job.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='';                  # where 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, Job.Name
155        
156  FROM $jobt AS Job, Client $filter $groupf
157 WHERE Job.ClientId = Client.ClientId
158   AND Job.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     use GD::Graph::colour qw(:colours);
195
196     if ($gtype eq 'lines') {
197         use GD::Graph::lines;
198         $graph = GD::Graph::lines->new ( $arg->{width}, $arg->{height} );
199
200     } elsif ($gtype eq 'bars') {
201         use GD::Graph::bars;
202         $graph = GD::Graph::bars->new ( $arg->{width}, $arg->{height} );
203
204     } elsif ($gtype eq 'linespoints') {
205         use GD::Graph::linespoints;
206         $graph = GD::Graph::linespoints->new ( $arg->{width}, $arg->{height} );
207
208 #   this doesnt works at this time
209 #    } elsif ($gtype eq 'bars3d') {
210 #       use GD::Graph::bars3d;
211 #       $graph = GD::Graph::bars3d->new ( $arg->{width}, $arg->{height} );
212
213     } else {
214         return undef;
215     }
216
217     $graph->set('x_label' => 'Time',
218                 'x_number_format' => sub { strftime('%D', localtime($_[0])) },
219                 'x_tick_number' => 5*$arg->{width}/800,
220                 'overwrite' => 1,
221                 dclrs => [ "lred", add_colour("#008e8e"), 
222                            add_colour("#afd8f8"), add_colour("#f6bd0f"),
223                            add_colour("#8bba00"), add_colour("#ff8e46"),
224                            add_colour("#d64646"),
225                            add_colour("#8e468e"), add_colour("#588526"),
226                            add_colour("#b3aa00"), add_colour("#008ed6"),
227                            add_colour("#9d080d"), add_colour("#a186be"),
228                 ],
229                 @options,
230                 );
231     if ($conf->{graph_font} && -f $conf->{graph_font}) {
232         $graph->set_title_font([$conf->{graph_font}], 12);
233         $graph->set_legend_font([$conf->{graph_font}], 11);
234     }
235     return $graph;
236 }
237
238 sub make_tab
239 {
240     my ($all_row) = @_;
241
242     my $i=0;
243     my $last_date=0;
244
245     my $ret = {};
246     
247     foreach my $row (@$all_row) {
248         my $label = $row->[1] . "/" . $row->[2] ; # client/backup name
249
250         if ($arg->{level} && $arg->{level} eq 'All') {  # can separate level
251             $label = $row->[4] . ': ' . $label;   # if users ask for
252         }
253
254         $ret->{date}->[$i]   = $row->[0];       
255         $ret->{$label}->[$i] = $row->[3];
256         $i++;
257         $last_date = $row->[0];
258     }
259
260     # insert a fake element
261     foreach my $elt ( keys %{$ret}) {
262         $ret->{$elt}->[$i] =  undef;
263     }
264
265     $ret->{date}->[$i] = $last_date + 1;
266
267     my $date = $ret->{date} ;
268     delete $ret->{date};
269
270     return ($date, $ret);
271 }
272
273 sub make_tab_sum
274 {
275     my ($all_row) = @_;
276
277     my $i=0;
278     my $last_date=0;
279
280     my $ret = {};
281     
282     foreach my $row (@$all_row) {
283         $ret->{date}->[$i]   = $row->[0];       
284         $ret->{nb}->[$i] = $row->[1];
285         $i++;
286     }
287
288     return ($ret);
289 }
290
291 if ($graph eq 'job_size') {
292
293     my $query = "
294 SELECT 
295        UNIX_TIMESTAMP(Job.StartTime)  AS starttime,
296        Client.Name                    AS clientname,
297        Job.Name                       AS jobname,
298        Job.JobBytes                   AS jobbytes,
299        Job.Level                      AS joblevel
300 FROM $jobt AS Job, FileSet, Client $filter $groupf
301 WHERE Job.ClientId = Client.ClientId
302   AND Job.FileSetId = FileSet.FileSetId
303   AND Job.Type = 'B'
304   $clientq
305   $statusq
306   $filesetq
307   $levelq
308   $jobnameq
309   $groupq
310 $limitq
311 ";
312
313     print STDERR $query if ($debug);
314
315     my $obj = get_graph('title' => "Job Size : $arg->{jclients}/$arg->{jjobnames}",
316                         'y_label' => 'Size',
317                         'y_min_value' => 0,
318                         'y_number_format' => \&Bweb::human_size,
319                         );
320
321     my $all = $dbh->selectall_arrayref($query) ;
322
323     my ($d, $ret) = make_tab($all);
324     if ($legend) {
325         $obj->set_legend(keys %$ret);
326     }
327     print $obj->plot([$d, values %$ret])->png;
328 }
329
330 if ($graph eq 'job_file') {
331
332     my $query = "
333 SELECT 
334        UNIX_TIMESTAMP(Job.StartTime)  AS starttime,
335        Client.Name                    AS clientname,
336        Job.Name                       AS jobname,
337        Job.JobFiles                   AS jobfiles,
338        Job.Level                      AS joblevel
339 FROM $jobt AS Job, FileSet, Client $filter $groupf
340 WHERE Job.ClientId = Client.ClientId
341   AND Job.FileSetId = FileSet.FileSetId
342   AND Job.Type = 'B'
343   $clientq
344   $statusq
345   $filesetq
346   $levelq
347   $jobnameq
348   $groupq
349 $limitq
350 ";
351
352     print STDERR $query if ($debug);
353
354     my $obj = get_graph('title' => "Job Files : $arg->{jclients}/$arg->{jjobnames}",
355                         'y_label' => 'Number Files',
356                         'y_min_value' => 0,
357                         );
358
359     my $all = $dbh->selectall_arrayref($query) ;
360
361     my ($d, $ret) = make_tab($all);
362     if ($legend) {
363         $obj->set_legend(keys %$ret);
364     }
365     print $obj->plot([$d, values %$ret])->png;
366 }
367
368 # it works only with postgresql at this time
369 # we dont use $jobt because we use File, so job is in Job table
370 elsif ($graph eq 'file_histo' and $arg->{where}) {
371     
372     my $dir  = $dbh->quote(dirname($arg->{where}) . '/');
373     my $file = $dbh->quote(basename($arg->{where}));
374
375     my $query = "
376 SELECT UNIX_TIMESTAMP(Job.StartTime)    AS starttime,
377        Client.Name                      AS client,
378        Job.Name                         AS jobname,
379        base64_decode_lstat(8,LStat)     AS lstat,
380        Job.Level                        AS joblevel
381
382 FROM Job, FileSet, Filename, Path, File, Client $filter
383 WHERE Job.ClientId = Client.ClientId
384   AND Job.FileSetId = FileSet.FileSetId
385   AND Job.Type = 'B'
386   AND File.JobId = Job.JobId
387   AND File.FilenameId = Filename.FilenameId
388   AND File.PathId = Path.PathId
389   AND Path.Path = $dir
390   AND Filename.Name = $file
391   $clientq
392   $statusq
393   $filesetq
394   $levelq
395   $jobnameq
396 $limitq
397 ";
398
399     print STDERR $query if ($debug);
400
401     my $all = $dbh->selectall_arrayref($query) ;
402
403     my $obj = get_graph('title' => "File size : $arg->{where}",
404                         'y_label' => 'File size',
405                         'y_min_value' => 0,
406                         'y_min_value' => 0,
407                         'y_number_format' => \&Bweb::human_size,
408                         );
409
410
411     my ($d, $ret) = make_tab($all);
412     if ($legend) {
413         $obj->set_legend(keys %$ret);
414     }
415     print $obj->plot([$d, values %$ret])->png;
416 }
417
418 # it works only with postgresql at this time
419 # TODO: use brestore_missing_path
420 elsif ($graph eq 'rep_histo' and $arg->{where}) {
421     
422     my $dir  = $arg->{where};
423     $dir .= '/' if ($dir !~ m!/$!);
424     $dir = $dbh->quote($dir);
425
426     my $query = "
427 SELECT UNIX_TIMESTAMP(Job.StartTime) AS starttime,
428        Client.Name                   AS client,
429        Job.Name                      AS jobname,
430        brestore_pathvisibility.size  AS size,
431        Job.Level                     AS joblevel
432
433 FROM Job, Client $filter, FileSet, Path, brestore_pathvisibility
434 WHERE Job.ClientId = Client.ClientId
435   AND Job.FileSetId = FileSet.FileSetId
436   AND Job.Type = 'B'
437   AND Job.JobId = brestore_pathvisibility.JobId
438   AND Path.PathId = brestore_pathvisibility.PathId
439   AND Path.Path = $dir
440   $clientq
441   $statusq
442   $filesetq
443   $levelq
444   $jobnameq
445 $limitq
446 ";
447
448     print STDERR $query if ($debug);
449
450     my $all = $dbh->selectall_arrayref($query) ;
451
452     my $obj = get_graph('title' => "Directory size : $arg->{where}",
453                         'y_label' => 'Directory size',
454                         'y_min_value' => 0,
455                         'y_min_value' => 0,
456                         'y_number_format' => \&Bweb::human_size,
457                         );
458
459
460     my ($d, $ret) = make_tab($all);
461     if ($legend) {
462         $obj->set_legend(keys %$ret);
463     }
464     print $obj->plot([$d, values %$ret])->png;
465 }
466
467 elsif ($graph eq 'job_rate') {
468
469     my $query = "
470 SELECT 
471        UNIX_TIMESTAMP(Job.StartTime)  AS starttime,
472        Client.Name                    AS clientname,
473        Job.Name                       AS jobname,
474        Job.JobBytes /
475        ($bweb->{sql}->{SEC_TO_INT}(
476                           $bweb->{sql}->{UNIX_TIMESTAMP}(EndTime)  
477                         - $bweb->{sql}->{UNIX_TIMESTAMP}(StartTime)) + 0.01) 
478          AS rate,
479        Job.Level                      AS joblevel
480
481 FROM $jobt AS Job, FileSet, Client $filter $groupf
482 WHERE Job.ClientId = Client.ClientId
483   AND Job.FileSetId = FileSet.FileSetId
484   AND Job.Type = 'B'
485   $clientq
486   $statusq
487   $filesetq
488   $levelq
489   $jobnameq
490   $groupq
491 $limitq
492 ";
493
494     print STDERR $query if ($debug);
495
496     my $obj = get_graph('title' => "Job Rate : $arg->{jclients}/$arg->{jjobnames}",
497                         'y_label' => 'Rate b/s',
498                         'y_min_value' => 0,
499                         'y_number_format' => \&Bweb::human_size,
500                         );
501
502     my $all = $dbh->selectall_arrayref($query) ;
503
504     my ($d, $ret) = make_tab($all);    
505     if ($legend) {
506         $obj->set_legend(keys %$ret);
507     }
508     print $obj->plot([$d, values %$ret])->png;
509 }
510
511
512
513 elsif ($graph eq 'job_duration') {
514
515     my $query = "
516 SELECT 
517        UNIX_TIMESTAMP(Job.StartTime)       AS starttime,
518        Client.Name                         AS clientname,
519        Job.Name                            AS jobname,
520   $bweb->{sql}->{SEC_TO_INT}(  $bweb->{sql}->{UNIX_TIMESTAMP}(EndTime)  
521                              - $bweb->{sql}->{UNIX_TIMESTAMP}(StartTime)) 
522          AS duration,
523        Job.Level                      AS joblevel
524
525 FROM $jobt AS Job, FileSet, Client $filter $groupf
526 WHERE Job.ClientId = Client.ClientId
527   AND Job.FileSetId = FileSet.FileSetId
528   AND Job.Type = 'B'
529   $clientq
530   $statusq
531   $filesetq
532   $levelq
533   $jobnameq
534   $groupq
535 $limitq
536 ";
537
538     print STDERR $query if ($debug);
539
540     my $obj = get_graph('title' => "Job Duration : $arg->{jclients}/$arg->{jjobnames}",
541                         'y_label' => 'Duration',
542                         'y_min_value' => 0,
543                         'y_number_format' => \&Bweb::human_sec,
544                         );
545     my $all = $dbh->selectall_arrayref($query) ;
546
547     my ($d, $ret) = make_tab($all);
548     if ($legend) {
549         $obj->set_legend(keys %$ret);
550     }
551     print $obj->plot([$d, values %$ret])->png;
552
553
554 # number of job per day/hour
555 } elsif ($graph =~ /^job_(count|sum|avg)_((p?)(day|hour|month))$/) {
556     my $t = $1;
557     my $d = uc($2);
558     my $per_t = $3;
559     my ($limit, $label) = $bweb->get_limit(age   => $arg->{age},
560                                            limit => $arg->{limit},
561                                            offset=> $arg->{offset},
562                                            groupby => "A",
563                                            order => "A",
564                                            );
565     my @arg;                    # arg for plotting
566
567     if (!$per_t) {              # much better aspect
568         #$gtype = 'lines';
569     } else {
570         push @arg, ("x_number_format" => undef,
571                     "x_min_value" => 0,
572                     );
573     }
574
575     if ($t eq 'sum' or $t eq 'avg') {
576         push @arg, ('y_number_format' => \&Bweb::human_size);
577     }
578     
579     my $stime = $bweb->{sql}->{"STARTTIME_$d"};
580
581     my $query = "
582 SELECT
583      " . ($per_t?"":"UNIX_TIMESTAMP") . "($stime) AS A,
584      $t(JobBytes)                  AS nb
585 FROM $jobt AS Job, FileSet, Client $filter $groupf
586 WHERE Job.ClientId = Client.ClientId
587   AND Job.FileSetId = FileSet.FileSetId
588   AND Job.Type = 'B'
589   $clientq
590   $statusq
591   $filesetq
592   $levelq
593   $jobnameq
594   $groupq
595 $limit
596 ";
597
598     print STDERR $query  if ($debug);
599
600     my $obj = get_graph('title' => "Job $t : $arg->{jclients}/$arg->{jjobnames}",
601                         'y_label' => $t,
602                         'y_min_value' => 0,
603                         @arg,
604                         );
605
606     my $all = $dbh->selectall_arrayref($query) ;
607 #    print STDERR Data::Dumper::Dumper($all);
608     my ($ret) = make_tab_sum($all);
609
610     print $obj->plot([$ret->{date}, $ret->{nb}])->png;    
611
612
613
614 $dbh->disconnect();