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