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