]> git.sur5r.net Git - bacula/bacula/blobdiff - gui/bweb/cgi/bgraph.pl
bweb: Update some GPL2 notice to AGPL
[bacula/bacula] / gui / bweb / cgi / bgraph.pl
index c691e2659706ea44acb31f14f9d1eb05aba6eb91..7407322dba4a5c3925973299c55d9a6b020ac373 100755 (executable)
@@ -3,22 +3,33 @@ use strict;
 
 =head1 LICENSE
 
-    Copyright (C) 2006 Eric Bollengier
-        All rights reserved.
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+   Bweb - A Bacula web interface
+   Bacula® - The Network Backup Solution
+
+   Copyright (C) 2000-2006 Free Software Foundation Europe e.V.
+
+   The main author of Bweb is Eric Bollengier.
+   The main author of Bacula is Kern Sibbald, with contributions from
+   many others, a complete list can be found in the file AUTHORS.
+   This program is Free Software; you can redistribute it and/or
+   modify it under the terms of version three of the GNU Affero General Public
+   License as published by the Free Software Foundation and included
+   in the file LICENSE.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+   Affero General Public License for more details.
+
+   You should have received a copy of the GNU Affero General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA.
+
+   Bacula® is a registered trademark of Kern Sibbald.
+   The licensor of Bacula is the Free Software Foundation Europe
+   (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
+   Switzerland, email:ftf@fsfeurope.org.
 
 =head1 VERSION
 
@@ -32,24 +43,33 @@ use Data::Dumper;
 use CGI;
 
 use POSIX qw/strftime/;
+use File::Basename qw/basename dirname/;
 
-my $conf = new Bweb::Config(config_file => '/etc/bweb/config');
+my $conf = new Bweb::Config(config_file => $Bweb::config_file);
 $conf->load();
-
 my $bweb = new Bweb(info => $conf);
 $bweb->connect_db();
 my $dbh = $bweb->{dbh};
 my $debug = $bweb->{debug};
 
-my $graph = CGI::param('graph') || 'begin';
+# Job table keep use Media or Job retention, so it's quite enought
+# for good statistics
+# CREATE TABLE JobHistory (LIKE Job);
+# INSERT INTO JobHistory
+#    (SELECT * FROM Job WHERE JobId NOT IN (SELECT JobId FROM JobHistory) );
+my $jobt = $bweb->get_stat_table();
+
+my $graph = CGI::param('graph') || 'job_size';
+my $legend = CGI::param('legend') || 'on' ;
+$legend = ($legend eq 'on')?1:0;
 
-my $arg = $bweb->get_form(qw/width height limit offset age
-                            jfilesets level status jjobnames jclients/);
+my $arg = $bweb->get_form(qw/width height limit offset age where jobid
+                            jfilesets level status jjobnames jclients jclient_groups/);
 
 my ($limitq, $label) = $bweb->get_limit(age   => $arg->{age},
                                        limit => $arg->{limit},
                                        offset=> $arg->{offset},
-                                       order => 'Job.StartTime ASC',
+                                       order => "Job.StartTime ASC",
                                        );
 
 my $statusq='';
@@ -58,7 +78,7 @@ if ($arg->{status} and $arg->{status} ne 'Any') {
 }
     
 my $levelq='';
-if ($arg->{level} and $arg->{level} ne 'Any') {
+if ($arg->{level} and $arg->{level} !~ 'All|Any') {
     $levelq = " AND Job.Level = '$arg->{level}' ";
 } 
 
@@ -81,14 +101,97 @@ if ($arg->{jclients}) {
     $arg->{jclients} = 'all';  # skip warning
 }
 
+my $groupf='';                 # from clause
+my $groupq='';                 # where clause
+if ($arg->{jclient_groups}) {
+    $groupf = " JOIN client_group_member ON (Client.ClientId = client_group_member.clientid) 
+                JOIN client_group USING (client_group_id)";
+    $groupq = " AND client_group_name IN ($arg->{jclient_groups}) ";
+}
+
+$bweb->can_do('r_view_stat');
+my $filter = $bweb->get_client_filter();
+
 my $gtype = CGI::param('gtype') || 'bars';
 
+
+# in this mode, we generate an image and an imagemap
+if ($gtype eq 'balloon') {
+    use Digest::MD5 qw(md5_hex);
+    use GBalloon;
+
+    my $b = new GBalloon(width=>$arg->{width}, 
+                        height =>$arg->{height});
+
+    my $order;
+    my %legend = (x_title => 'Time', 
+                 x_func => sub { 
+                     POSIX::strftime('%H:%M', gmtime($_[0])) 
+                     }
+                 ) ;
+    if ($graph eq 'job_time_size') {
+       $order = 'JobFiles,JobBytes';
+
+       $legend{y_title} = 'Nb files';
+       $legend{y_func} = sub { int(shift)};
+       $legend{z_title} = 'Size';
+       $legend{z_func} = \&Bweb::human_size;
+    } else {
+       $order = 'JobBytes,JobFiles';
+
+       $legend{y_title} = 'Size';
+       $legend{y_func} = \&Bweb::human_size;
+       $legend{z_title} = 'Nb files';
+       $legend{z_func} = sub { int(shift)};
+    }
+
+    $b->set_legend_axis(%legend);
+
+    my $all = $dbh->selectall_arrayref("
+SELECT $bweb->{sql}->{SEC_TO_INT}(  $bweb->{sql}->{UNIX_TIMESTAMP}(EndTime)
+                                  - $bweb->{sql}->{UNIX_TIMESTAMP}(StartTime))
+         AS duration, $order, JobId, Job.Name
+       
+ FROM $jobt AS Job, Client $filter $groupf
+WHERE Job.ClientId = Client.ClientId
+  AND Job.Type = 'B'
+  $clientq
+  $statusq
+  $levelq
+  $jobnameq
+  $groupq
+$limitq
+");
+
+    foreach my $a (@$all) {
+       $b->add_point($a->[0], $a->[1], $a->[2], 
+                     "?action=job_zoom;jobid=$a->[3]",
+                     "$a->[4] $legend{z_title} " . $legend{z_func}($a->[2]));
+    }
+    
+    $b->init_gd();
+    $b->finalize();
+
+    my $md5_rep = md5_hex(join(":", map { $arg->{$_} } sort keys %$arg));
+
+    # need to cleanup this path
+    open(FP, ">$conf->{fv_write_path}/$md5_rep.png");
+    print FP $GBalloon::gd->png;
+    close(FP);
+    
+    print $b->get_imagemap("Job overview", "/bweb/fv/$md5_rep.png");
+
+    exit 0;
+}
+
 print CGI::header('image/png');
 
 sub get_graph
 {
     my (@options) = @_;
     my $graph;
+    use GD::Graph::colour qw(:colours);
+
     if ($gtype eq 'lines') {
        use GD::Graph::lines;
        $graph = GD::Graph::lines->new ( $arg->{width}, $arg->{height} );
@@ -101,9 +204,10 @@ sub get_graph
        use GD::Graph::linespoints;
        $graph = GD::Graph::linespoints->new ( $arg->{width}, $arg->{height} );
 
-    } elsif ($gtype eq 'bars3d') {
-       use GD::Graph::bars3d;
-       $graph = GD::Graph::bars3d->new ( $arg->{width}, $arg->{height} );
+#   this doesnt works at this time
+#    } elsif ($gtype eq 'bars3d') {
+#      use GD::Graph::bars3d;
+#      $graph = GD::Graph::bars3d->new ( $arg->{width}, $arg->{height} );
 
     } else {
        return undef;
@@ -111,10 +215,22 @@ sub get_graph
 
     $graph->set('x_label' => 'Time',
                'x_number_format' => sub { strftime('%D', localtime($_[0])) },
-               'x_tick_number' => 1,
+               'x_tick_number' => 5*$arg->{width}/800,
+                'overwrite' => 1,
+                dclrs => [ "lred", add_colour("#008e8e"), 
+                           add_colour("#afd8f8"), add_colour("#f6bd0f"),
+                           add_colour("#8bba00"), add_colour("#ff8e46"),
+                           add_colour("#d64646"),
+                           add_colour("#8e468e"), add_colour("#588526"),
+                           add_colour("#b3aa00"), add_colour("#008ed6"),
+                           add_colour("#9d080d"), add_colour("#a186be"),
+                ],
                @options,
                );
-
+    if ($conf->{graph_font} && -f $conf->{graph_font}) {
+        $graph->set_title_font([$conf->{graph_font}], 12);
+        $graph->set_legend_font([$conf->{graph_font}], 11);
+    }
     return $graph;
 }
 
@@ -130,6 +246,10 @@ sub make_tab
     foreach my $row (@$all_row) {
        my $label = $row->[1] . "/" . $row->[2] ; # client/backup name
 
+       if ($arg->{level} && $arg->{level} eq 'All') {  # can separate level
+           $label = $row->[4] . ': ' . $label;   # if users ask for
+       }
+
        $ret->{date}->[$i]   = $row->[0];       
        $ret->{$label}->[$i] = $row->[3];
        $i++;
@@ -149,15 +269,34 @@ sub make_tab
     return ($date, $ret);
 }
 
+sub make_tab_sum
+{
+    my ($all_row) = @_;
+
+    my $i=0;
+    my $last_date=0;
+
+    my $ret = {};
+    
+    foreach my $row (@$all_row) {
+       $ret->{date}->[$i]   = $row->[0];       
+       $ret->{nb}->[$i] = $row->[1];
+       $i++;
+    }
+
+    return ($ret);
+}
+
 if ($graph eq 'job_size') {
 
     my $query = "
 SELECT 
-       UNIX_TIMESTAMP(Job.StartTime)    AS starttime,
-       Client.Name                      AS clientname,
-       Job.Name                         AS jobname,
-       Job.JobBytes                     AS jobbytes
-FROM Job, Client, FileSet
+       UNIX_TIMESTAMP(Job.StartTime)  AS starttime,
+       Client.Name                    AS clientname,
+       Job.Name                       AS jobname,
+       Job.JobBytes                   AS jobbytes,
+       Job.Level                      AS joblevel
+FROM $jobt AS Job, FileSet, Client $filter $groupf
 WHERE Job.ClientId = Client.ClientId
   AND Job.FileSetId = FileSet.FileSetId
   AND Job.Type = 'B'
@@ -166,6 +305,7 @@ WHERE Job.ClientId = Client.ClientId
   $filesetq
   $levelq
   $jobnameq
+  $groupq
 $limitq
 ";
 
@@ -180,7 +320,9 @@ $limitq
     my $all = $dbh->selectall_arrayref($query) ;
 
     my ($d, $ret) = make_tab($all);
-    $obj->set_legend(keys %$ret);
+    if ($legend) {
+       $obj->set_legend(keys %$ret);
+    }
     print $obj->plot([$d, values %$ret])->png;
 }
 
@@ -188,11 +330,12 @@ if ($graph eq 'job_file') {
 
     my $query = "
 SELECT 
-       UNIX_TIMESTAMP(Job.StartTime)    AS starttime,
-       Client.Name                      AS clientname,
-       Job.Name                         AS jobname,
-       Job.JobFiles                     AS jobfiles
-FROM Job, Client, FileSet
+       UNIX_TIMESTAMP(Job.StartTime)  AS starttime,
+       Client.Name                    AS clientname,
+       Job.Name                       AS jobname,
+       Job.JobFiles                   AS jobfiles,
+       Job.Level                      AS joblevel
+FROM $jobt AS Job, FileSet, Client $filter $groupf
 WHERE Job.ClientId = Client.ClientId
   AND Job.FileSetId = FileSet.FileSetId
   AND Job.Type = 'B'
@@ -201,6 +344,7 @@ WHERE Job.ClientId = Client.ClientId
   $filesetq
   $levelq
   $jobnameq
+  $groupq
 $limitq
 ";
 
@@ -214,7 +358,108 @@ $limitq
     my $all = $dbh->selectall_arrayref($query) ;
 
     my ($d, $ret) = make_tab($all);
-    $obj->set_legend(keys %$ret);
+    if ($legend) {
+       $obj->set_legend(keys %$ret);
+    }
+    print $obj->plot([$d, values %$ret])->png;
+}
+
+# it works only with postgresql at this time
+# we don't use $jobt because we use File, so job is in Job table
+elsif ($graph eq 'file_histo' and $arg->{where}) {
+    
+    my $dir  = $dbh->quote(dirname($arg->{where}) . '/');
+    my $file = $dbh->quote(basename($arg->{where}));
+
+    my $query = "
+SELECT UNIX_TIMESTAMP(Job.StartTime)    AS starttime,
+       Client.Name                      AS client,
+       Job.Name                         AS jobname,
+       base64_decode_lstat(8,LStat)     AS lstat,
+       Job.Level                        AS joblevel
+
+FROM Job, FileSet, Filename, Path, File, Client $filter
+WHERE Job.ClientId = Client.ClientId
+  AND Job.FileSetId = FileSet.FileSetId
+  AND Job.Type = 'B'
+  AND File.JobId = Job.JobId
+  AND File.FilenameId = Filename.FilenameId
+  AND File.PathId = Path.PathId
+  AND Path.Path = $dir
+  AND Filename.Name = $file
+  $clientq
+  $statusq
+  $filesetq
+  $levelq
+  $jobnameq
+$limitq
+";
+
+    print STDERR $query if ($debug);
+
+    my $all = $dbh->selectall_arrayref($query) ;
+
+    my $obj = get_graph('title' => "File size : $arg->{where}",
+                       'y_label' => 'File size',
+                       'y_min_value' => 0,
+                       'y_min_value' => 0,
+                       'y_number_format' => \&Bweb::human_size,
+                       );
+
+
+    my ($d, $ret) = make_tab($all);
+    if ($legend) {
+       $obj->set_legend(keys %$ret);
+    }
+    print $obj->plot([$d, values %$ret])->png;
+}
+
+# it works only with postgresql at this time
+# TODO: use brestore_missing_path
+elsif ($graph eq 'rep_histo' and $arg->{where}) {
+    
+    my $dir  = $arg->{where};
+    $dir .= '/' if ($dir !~ m!/$!);
+    $dir = $dbh->quote($dir);
+
+    my $query = "
+SELECT UNIX_TIMESTAMP(Job.StartTime) AS starttime,
+       Client.Name                   AS client,
+       Job.Name                      AS jobname,
+       brestore_pathvisibility.size  AS size,
+       Job.Level                     AS joblevel
+
+FROM Job, Client $filter, FileSet, Path, brestore_pathvisibility
+WHERE Job.ClientId = Client.ClientId
+  AND Job.FileSetId = FileSet.FileSetId
+  AND Job.Type = 'B'
+  AND Job.JobId = brestore_pathvisibility.JobId
+  AND Path.PathId = brestore_pathvisibility.PathId
+  AND Path.Path = $dir
+  $clientq
+  $statusq
+  $filesetq
+  $levelq
+  $jobnameq
+$limitq
+";
+
+    print STDERR $query if ($debug);
+
+    my $all = $dbh->selectall_arrayref($query) ;
+
+    my $obj = get_graph('title' => "Directory size : $arg->{where}",
+                       'y_label' => 'Directory size',
+                       'y_min_value' => 0,
+                       'y_min_value' => 0,
+                       'y_number_format' => \&Bweb::human_size,
+                       );
+
+
+    my ($d, $ret) = make_tab($all);
+    if ($legend) {
+       $obj->set_legend(keys %$ret);
+    }
     print $obj->plot([$d, values %$ret])->png;
 }
 
@@ -222,16 +467,17 @@ elsif ($graph eq 'job_rate') {
 
     my $query = "
 SELECT 
-       UNIX_TIMESTAMP(Job.StartTime)                          AS starttime,
-       Client.Name                      AS clientname,
-       Job.Name                         AS jobname,
+       UNIX_TIMESTAMP(Job.StartTime)  AS starttime,
+       Client.Name                    AS clientname,
+       Job.Name                       AS jobname,
        Job.JobBytes /
        ($bweb->{sql}->{SEC_TO_INT}(
                           $bweb->{sql}->{UNIX_TIMESTAMP}(EndTime)  
                         - $bweb->{sql}->{UNIX_TIMESTAMP}(StartTime)) + 0.01) 
-         AS rate
+         AS rate,
+       Job.Level                      AS joblevel
 
-FROM Job, Client, FileSet
+FROM $jobt AS Job, FileSet, Client $filter $groupf
 WHERE Job.ClientId = Client.ClientId
   AND Job.FileSetId = FileSet.FileSetId
   AND Job.Type = 'B'
@@ -240,6 +486,7 @@ WHERE Job.ClientId = Client.ClientId
   $filesetq
   $levelq
   $jobnameq
+  $groupq
 $limitq
 ";
 
@@ -254,7 +501,9 @@ $limitq
     my $all = $dbh->selectall_arrayref($query) ;
 
     my ($d, $ret) = make_tab($all);    
-    $obj->set_legend(keys %$ret);
+    if ($legend) {
+       $obj->set_legend(keys %$ret);
+    }
     print $obj->plot([$d, values %$ret])->png;
 }
 
@@ -264,13 +513,15 @@ elsif ($graph eq 'job_duration') {
 
     my $query = "
 SELECT 
-       UNIX_TIMESTAMP(Job.StartTime)                           AS starttime,
-       Client.Name                                             AS clientname,
-       Job.Name                                                AS jobname,
+       UNIX_TIMESTAMP(Job.StartTime)       AS starttime,
+       Client.Name                         AS clientname,
+       Job.Name                            AS jobname,
   $bweb->{sql}->{SEC_TO_INT}(  $bweb->{sql}->{UNIX_TIMESTAMP}(EndTime)  
                              - $bweb->{sql}->{UNIX_TIMESTAMP}(StartTime)) 
-         AS duration
-FROM Job, Client, FileSet
+         AS duration,
+       Job.Level                      AS joblevel
+
+FROM $jobt AS Job, FileSet, Client $filter $groupf
 WHERE Job.ClientId = Client.ClientId
   AND Job.FileSetId = FileSet.FileSetId
   AND Job.Type = 'B'
@@ -279,6 +530,7 @@ WHERE Job.ClientId = Client.ClientId
   $filesetq
   $levelq
   $jobnameq
+  $groupq
 $limitq
 ";
 
@@ -292,7 +544,70 @@ $limitq
     my $all = $dbh->selectall_arrayref($query) ;
 
     my ($d, $ret) = make_tab($all);
-    $obj->set_legend(keys %$ret);
+    if ($legend) {
+       $obj->set_legend(keys %$ret);
+    }
     print $obj->plot([$d, values %$ret])->png;
-}
 
+
+# number of job per day/hour
+} elsif ($graph =~ /^job_(count|sum|avg)_((p?)(day|hour|month))$/) {
+    my $t = $1;
+    my $d = uc($2);
+    my $per_t = $3;
+    my ($limit, $label) = $bweb->get_limit(age   => $arg->{age},
+                                          limit => $arg->{limit},
+                                          offset=> $arg->{offset},
+                                          groupby => "A",
+                                          order => "A",
+                                          );
+    my @arg;                   # arg for plotting
+
+    if (!$per_t) {             # much better aspect
+       #$gtype = 'lines';
+    } else {
+       push @arg, ("x_number_format" => undef,
+                   "x_min_value" => 0,
+                   );
+    }
+
+    if ($t eq 'sum' or $t eq 'avg') {
+       push @arg, ('y_number_format' => \&Bweb::human_size);
+    }
+    
+    my $stime = $bweb->{sql}->{"STARTTIME_$d"};
+
+    my $query = "
+SELECT
+     " . ($per_t?"":"UNIX_TIMESTAMP") . "($stime) AS A,
+     $t(JobBytes)                  AS nb
+FROM $jobt AS Job, FileSet, Client $filter $groupf
+WHERE Job.ClientId = Client.ClientId
+  AND Job.FileSetId = FileSet.FileSetId
+  AND Job.Type = 'B'
+  $clientq
+  $statusq
+  $filesetq
+  $levelq
+  $jobnameq
+  $groupq
+$limit
+";
+
+    print STDERR $query  if ($debug);
+
+    my $obj = get_graph('title' => "Job $t : $arg->{jclients}/$arg->{jjobnames}",
+                       'y_label' => $t,
+                       'y_min_value' => 0,
+                       @arg,
+                       );
+
+    my $all = $dbh->selectall_arrayref($query) ;
+#    print STDERR Data::Dumper::Dumper($all);
+    my ($ret) = make_tab_sum($all);
+
+    print $obj->plot([$ret->{date}, $ret->{nb}])->png;    
+
+} 
+
+$dbh->disconnect();