]> git.sur5r.net Git - bacula/bacula/blob - gui/bweb/cgi/bgraph.pl
ebl update po
[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 $all = $dbh->selectall_arrayref("
125 SELECT $bweb->{sql}->{SEC_TO_INT}(  $bweb->{sql}->{UNIX_TIMESTAMP}(EndTime)
126                                   - $bweb->{sql}->{UNIX_TIMESTAMP}(StartTime))
127          AS duration, JobBytes, JobFiles, JobId, $jobt.Name
128        
129  FROM $jobt, Client $filter $groupf
130 WHERE $jobt.ClientId = Client.ClientId
131   AND $jobt.Type = 'B'
132   $clientq
133   $statusq
134   $levelq
135   $jobnameq
136   $groupq
137 $limitq
138 ");
139
140     my $b = new GBalloon(width=>$arg->{width}, 
141                          height =>$arg->{height});
142     $b->set_legend_axis(x_title => 'Time', 
143                         x_func => sub { 
144                             POSIX::strftime('%H:%M', gmtime($_[0])) 
145                             },
146                         y_title => 'Size', y_func => \&Bweb::human_size,
147                         z_title => 'Nb files');
148
149     foreach my $a (@$all) {
150         $b->add_point($a->[0], $a->[1], $a->[2], 
151                       "?action=job_zoom;jobid=$a->[3]",
152                       "$a->[4] $a->[2] files");
153     }
154     
155     $b->init_gd();
156     $b->finalize();
157
158     my $md5_rep = md5_hex(join(":", map { $arg->{$_} } sort keys %$arg));
159
160     # need to cleanup this path
161     open(FP, ">$conf->{fv_write_path}/$md5_rep.png");
162     print FP $GBalloon::gd->png;
163     close(FP);
164     
165     print $b->get_imagemap("Job overview", "/bweb/fv/$md5_rep.png");
166
167     exit 0;
168 }
169
170 print CGI::header('image/png');
171
172 sub get_graph
173 {
174     my (@options) = @_;
175     my $graph;
176     if ($gtype eq 'lines') {
177         use GD::Graph::lines;
178         $graph = GD::Graph::lines->new ( $arg->{width}, $arg->{height} );
179
180     } elsif ($gtype eq 'bars') {
181         use GD::Graph::bars;
182         $graph = GD::Graph::bars->new ( $arg->{width}, $arg->{height} );
183
184     } elsif ($gtype eq 'linespoints') {
185         use GD::Graph::linespoints;
186         $graph = GD::Graph::linespoints->new ( $arg->{width}, $arg->{height} );
187
188 #   this doesnt works at this time
189 #    } elsif ($gtype eq 'bars3d') {
190 #       use GD::Graph::bars3d;
191 #       $graph = GD::Graph::bars3d->new ( $arg->{width}, $arg->{height} );
192
193     } else {
194         return undef;
195     }
196
197     $graph->set('x_label' => 'Time',
198                 'x_number_format' => sub { strftime('%D', localtime($_[0])) },
199                 'x_tick_number' => 1,
200                 @options,
201                 );
202
203     return $graph;
204 }
205
206 sub make_tab
207 {
208     my ($all_row) = @_;
209
210     my $i=0;
211     my $last_date=0;
212
213     my $ret = {};
214     
215     foreach my $row (@$all_row) {
216         my $label = $row->[1] . "/" . $row->[2] ; # client/backup name
217
218         $ret->{date}->[$i]   = $row->[0];       
219         $ret->{$label}->[$i] = $row->[3];
220         $i++;
221         $last_date = $row->[0];
222     }
223
224     # insert a fake element
225     foreach my $elt ( keys %{$ret}) {
226         $ret->{$elt}->[$i] =  undef;
227     }
228
229     $ret->{date}->[$i] = $last_date + 1;
230
231     my $date = $ret->{date} ;
232     delete $ret->{date};
233
234     return ($date, $ret);
235 }
236
237 sub make_tab_sum
238 {
239     my ($all_row) = @_;
240
241     my $i=0;
242     my $last_date=0;
243
244     my $ret = {};
245     
246     foreach my $row (@$all_row) {
247         $ret->{date}->[$i]   = $row->[0];       
248         $ret->{nb}->[$i] = $row->[1];
249         $i++;
250     }
251
252     return ($ret);
253 }
254
255 if ($graph eq 'job_size') {
256
257     my $query = "
258 SELECT 
259        UNIX_TIMESTAMP($jobt.StartTime)  AS starttime,
260        Client.Name                      AS clientname,
261        $jobt.Name                       AS jobname,
262        $jobt.JobBytes                   AS jobbytes
263 FROM $jobt, FileSet, Client $filter $groupf
264 WHERE $jobt.ClientId = Client.ClientId
265   AND $jobt.FileSetId = FileSet.FileSetId
266   AND $jobt.Type = 'B'
267   $clientq
268   $statusq
269   $filesetq
270   $levelq
271   $jobnameq
272   $groupq
273 $limitq
274 ";
275
276     print STDERR $query if ($debug);
277
278     my $obj = get_graph('title' => "Job Size : $arg->{jclients}/$arg->{jjobnames}",
279                         'y_label' => 'Size',
280                         'y_min_value' => 0,
281                         'y_number_format' => \&Bweb::human_size,
282                         );
283
284     my $all = $dbh->selectall_arrayref($query) ;
285
286     my ($d, $ret) = make_tab($all);
287     if ($legend) {
288         $obj->set_legend(keys %$ret);
289     }
290     print $obj->plot([$d, values %$ret])->png;
291 }
292
293 if ($graph eq 'job_file') {
294
295     my $query = "
296 SELECT 
297        UNIX_TIMESTAMP($jobt.StartTime)  AS starttime,
298        Client.Name                      AS clientname,
299        $jobt.Name                       AS jobname,
300        $jobt.JobFiles                   AS jobfiles
301 FROM $jobt, FileSet, Client $filter $groupf
302 WHERE $jobt.ClientId = Client.ClientId
303   AND $jobt.FileSetId = FileSet.FileSetId
304   AND $jobt.Type = 'B'
305   $clientq
306   $statusq
307   $filesetq
308   $levelq
309   $jobnameq
310   $groupq
311 $limitq
312 ";
313
314     print STDERR $query if ($debug);
315
316     my $obj = get_graph('title' => "Job Files : $arg->{jclients}/$arg->{jjobnames}",
317                         'y_label' => 'Number Files',
318                         'y_min_value' => 0,
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 # it works only with postgresql at this time
331 # we dont use $jobt because we use File, so job is in Job table
332 elsif ($graph eq 'file_histo' and $arg->{where}) {
333     
334     my $dir  = $dbh->quote(dirname($arg->{where}) . '/');
335     my $file = $dbh->quote(basename($arg->{where}));
336
337     my $query = "
338 SELECT UNIX_TIMESTAMP(Job.StartTime)    AS starttime,
339        Client.Name                      AS client,
340        Job.Name                         AS jobname,
341        base64_decode_lstat(8,LStat)     AS lstat
342
343 FROM Job, FileSet, Filename, Path, File, Client $filter
344 WHERE Job.ClientId = Client.ClientId
345   AND Job.FileSetId = FileSet.FileSetId
346   AND Job.Type = 'B'
347   AND File.JobId = Job.JobId
348   AND File.FilenameId = Filename.FilenameId
349   AND File.PathId = Path.PathId
350   AND Path.Path = $dir
351   AND Filename.Name = $file
352   $clientq
353   $statusq
354   $filesetq
355   $levelq
356   $jobnameq
357 $limitq
358 ";
359
360     print STDERR $query if ($debug);
361
362     my $all = $dbh->selectall_arrayref($query) ;
363
364     my $obj = get_graph('title' => "File size : $arg->{where}",
365                         'y_label' => 'File size',
366                         'y_min_value' => 0,
367                         'y_min_value' => 0,
368                         'y_number_format' => \&Bweb::human_size,
369                         );
370
371
372     my ($d, $ret) = make_tab($all);
373     if ($legend) {
374         $obj->set_legend(keys %$ret);
375     }
376     print $obj->plot([$d, values %$ret])->png;
377 }
378
379 # it works only with postgresql at this time
380 # TODO: use brestore_missing_path
381 elsif ($graph eq 'rep_histo' and $arg->{where}) {
382     
383     my $dir  = $arg->{where};
384     $dir .= '/' if ($dir !~ m!/$!);
385     $dir = $dbh->quote($dir);
386
387     my $query = "
388 SELECT UNIX_TIMESTAMP(Job.StartTime) AS starttime,
389        Client.Name                   AS client,
390        Job.Name                      AS jobname,
391        brestore_pathvisibility.size  AS size
392
393 FROM Job, Client $filter, FileSet, Path, brestore_pathvisibility
394 WHERE Job.ClientId = Client.ClientId
395   AND Job.FileSetId = FileSet.FileSetId
396   AND Job.Type = 'B'
397   AND Job.JobId = brestore_pathvisibility.JobId
398   AND Path.PathId = brestore_pathvisibility.PathId
399   AND Path.Path = $dir
400   $clientq
401   $statusq
402   $filesetq
403   $levelq
404   $jobnameq
405 $limitq
406 ";
407
408     print STDERR $query if ($debug);
409
410     my $all = $dbh->selectall_arrayref($query) ;
411
412     my $obj = get_graph('title' => "Directory size : $arg->{where}",
413                         'y_label' => 'Directory size',
414                         'y_min_value' => 0,
415                         'y_min_value' => 0,
416                         'y_number_format' => \&Bweb::human_size,
417                         );
418
419
420     my ($d, $ret) = make_tab($all);
421     if ($legend) {
422         $obj->set_legend(keys %$ret);
423     }
424     print $obj->plot([$d, values %$ret])->png;
425 }
426
427 elsif ($graph eq 'job_rate') {
428
429     my $query = "
430 SELECT 
431        UNIX_TIMESTAMP($jobt.StartTime)  AS starttime,
432        Client.Name                      AS clientname,
433        $jobt.Name                       AS jobname,
434        $jobt.JobBytes /
435        ($bweb->{sql}->{SEC_TO_INT}(
436                           $bweb->{sql}->{UNIX_TIMESTAMP}(EndTime)  
437                         - $bweb->{sql}->{UNIX_TIMESTAMP}(StartTime)) + 0.01) 
438          AS rate
439
440 FROM $jobt, FileSet, Client $filter $groupf
441 WHERE $jobt.ClientId = Client.ClientId
442   AND $jobt.FileSetId = FileSet.FileSetId
443   AND $jobt.Type = 'B'
444   $clientq
445   $statusq
446   $filesetq
447   $levelq
448   $jobnameq
449   $groupq
450 $limitq
451 ";
452
453     print STDERR $query if ($debug);
454
455     my $obj = get_graph('title' => "Job Rate : $arg->{jclients}/$arg->{jjobnames}",
456                         'y_label' => 'Rate b/s',
457                         'y_min_value' => 0,
458                         'y_number_format' => \&Bweb::human_size,
459                         );
460
461     my $all = $dbh->selectall_arrayref($query) ;
462
463     my ($d, $ret) = make_tab($all);    
464     if ($legend) {
465         $obj->set_legend(keys %$ret);
466     }
467     print $obj->plot([$d, values %$ret])->png;
468 }
469
470
471
472 elsif ($graph eq 'job_duration') {
473
474     my $query = "
475 SELECT 
476        UNIX_TIMESTAMP($jobt.StartTime)                         AS starttime,
477        Client.Name                                             AS clientname,
478        $jobt.Name                                              AS jobname,
479   $bweb->{sql}->{SEC_TO_INT}(  $bweb->{sql}->{UNIX_TIMESTAMP}(EndTime)  
480                              - $bweb->{sql}->{UNIX_TIMESTAMP}(StartTime)) 
481          AS duration
482 FROM $jobt, FileSet, Client $filter $groupf
483 WHERE $jobt.ClientId = Client.ClientId
484   AND $jobt.FileSetId = FileSet.FileSetId
485   AND $jobt.Type = 'B'
486   $clientq
487   $statusq
488   $filesetq
489   $levelq
490   $jobnameq
491   $groupq
492 $limitq
493 ";
494
495     print STDERR $query if ($debug);
496
497     my $obj = get_graph('title' => "Job Duration : $arg->{jclients}/$arg->{jjobnames}",
498                         'y_label' => 'Duration',
499                         'y_min_value' => 0,
500                         'y_number_format' => \&Bweb::human_sec,
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 # number of job per day/hour
512 } elsif ($graph =~ /^job_(count|sum|avg)_((p?)(day|hour|month))$/) {
513     my $t = $1;
514     my $d = uc($2);
515     my $per_t = $3;
516     my ($limit, $label) = $bweb->get_limit(age   => $arg->{age},
517                                            limit => $arg->{limit},
518                                            offset=> $arg->{offset},
519                                            groupby => "A",
520                                            order => "A",
521                                            );
522     my @arg;                    # arg for plotting
523
524     if (!$per_t) {              # much better aspect
525         #$gtype = 'lines';
526     } else {
527         push @arg, ("x_number_format" => undef,
528                     "x_min_value" => 0,
529                     );
530     }
531
532     if ($t eq 'sum' or $t eq 'avg') {
533         push @arg, ('y_number_format' => \&Bweb::human_size);
534     }
535     
536     my $stime = $bweb->{sql}->{"STARTTIME_$d"};
537     $stime =~ s/Job\./$jobt\./;
538
539     my $query = "
540 SELECT
541      " . ($per_t?"":"UNIX_TIMESTAMP") . "($stime) AS A,
542      $t(JobBytes)                  AS nb
543 FROM $jobt, FileSet, Client $filter $groupf
544 WHERE $jobt.ClientId = Client.ClientId
545   AND $jobt.FileSetId = FileSet.FileSetId
546   AND $jobt.Type = 'B'
547   $clientq
548   $statusq
549   $filesetq
550   $levelq
551   $jobnameq
552   $groupq
553 $limit
554 ";
555
556     print STDERR $query  if ($debug);
557
558     my $obj = get_graph('title' => "Job $t : $arg->{jclients}/$arg->{jjobnames}",
559                         'y_label' => $t,
560                         'y_min_value' => 0,
561                         @arg,
562                         );
563
564     my $all = $dbh->selectall_arrayref($query) ;
565 #    print STDERR Data::Dumper::Dumper($all);
566     my ($ret) = make_tab_sum($all);
567
568     print $obj->plot([$ret->{date}, $ret->{nb}])->png;    
569
570
571
572 $dbh->disconnect();