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