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