6 Bweb - A Bacula web interface
7 Bacula® - The Network Backup Solution
9 Copyright (C) 2000-2006 Free Software Foundation Europe e.V.
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.
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.
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.
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
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.
46 use POSIX qw/strftime/;
47 use File::Basename qw/basename dirname/;
49 my $conf = new Bweb::Config(config_file => $Bweb::config_file);
51 my $bweb = new Bweb(info => $conf);
53 my $dbh = $bweb->{dbh};
54 my $debug = $bweb->{debug};
56 # Job table keep use Media or Job retention, so it's quite enought
58 # CREATE TABLE job_old (LIKE Job);
60 # (SELECT * FROM Job WHERE JobId NOT IN (SELECT JobId FROM job_old) );
61 my $jobt = $conf->{stat_job_table} || 'Job';
63 my $graph = CGI::param('graph') || 'job_size';
64 my $legend = CGI::param('legend') || 'on' ;
65 $legend = ($legend eq 'on')?1:0;
67 my $arg = $bweb->get_form(qw/width height limit offset age where jobid
68 jfilesets level status jjobnames jclients jclient_groups/);
70 my ($limitq, $label) = $bweb->get_limit(age => $arg->{age},
71 limit => $arg->{limit},
72 offset=> $arg->{offset},
73 order => "$jobt.StartTime ASC",
77 if ($arg->{status} and $arg->{status} ne 'Any') {
78 $statusq = " AND $jobt.JobStatus = '$arg->{status}' ";
82 if ($arg->{level} and $arg->{level} ne 'Any') {
83 $levelq = " AND $jobt.Level = '$arg->{level}' ";
87 if ($arg->{jfilesets}) {
88 $filesetq = " AND FileSet.FileSet IN ($arg->{qfilesets}) ";
92 if ($arg->{jjobnames}) {
93 $jobnameq = " AND $jobt.Name IN ($arg->{jjobnames}) ";
95 $arg->{jjobnames} = 'all'; # skip warning
99 if ($arg->{jclients}) {
100 $clientq = " AND Client.Name IN ($arg->{jclients}) ";
102 $arg->{jclients} = 'all'; # skip warning
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}) ";
113 my $filter = $bweb->get_client_filter();
115 my $gtype = CGI::param('gtype') || 'bars';
117 print CGI::header('image/png');
123 if ($gtype eq 'lines') {
124 use GD::Graph::lines;
125 $graph = GD::Graph::lines->new ( $arg->{width}, $arg->{height} );
127 } elsif ($gtype eq 'bars') {
129 $graph = GD::Graph::bars->new ( $arg->{width}, $arg->{height} );
131 } elsif ($gtype eq 'linespoints') {
132 use GD::Graph::linespoints;
133 $graph = GD::Graph::linespoints->new ( $arg->{width}, $arg->{height} );
135 # this doesnt works at this time
136 # } elsif ($gtype eq 'bars3d') {
137 # use GD::Graph::bars3d;
138 # $graph = GD::Graph::bars3d->new ( $arg->{width}, $arg->{height} );
144 $graph->set('x_label' => 'Time',
145 'x_number_format' => sub { strftime('%D', localtime($_[0])) },
146 'x_tick_number' => 1,
162 foreach my $row (@$all_row) {
163 my $label = $row->[1] . "/" . $row->[2] ; # client/backup name
165 $ret->{date}->[$i] = $row->[0];
166 $ret->{$label}->[$i] = $row->[3];
168 $last_date = $row->[0];
171 # insert a fake element
172 foreach my $elt ( keys %{$ret}) {
173 $ret->{$elt}->[$i] = undef;
176 $ret->{date}->[$i] = $last_date + 1;
178 my $date = $ret->{date} ;
181 return ($date, $ret);
193 foreach my $row (@$all_row) {
194 $ret->{date}->[$i] = $row->[0];
195 $ret->{nb}->[$i] = $row->[1];
202 if ($graph eq 'job_size') {
206 UNIX_TIMESTAMP($jobt.StartTime) AS starttime,
207 Client.Name AS clientname,
208 $jobt.Name AS jobname,
209 $jobt.JobBytes AS jobbytes
210 FROM $jobt, FileSet, Client $filter $groupf
211 WHERE $jobt.ClientId = Client.ClientId
212 AND $jobt.FileSetId = FileSet.FileSetId
223 print STDERR $query if ($debug);
225 my $obj = get_graph('title' => "Job Size : $arg->{jclients}/$arg->{jjobnames}",
228 'y_number_format' => \&Bweb::human_size,
231 my $all = $dbh->selectall_arrayref($query) ;
233 my ($d, $ret) = make_tab($all);
235 $obj->set_legend(keys %$ret);
237 print $obj->plot([$d, values %$ret])->png;
240 if ($graph eq 'job_file') {
244 UNIX_TIMESTAMP($jobt.StartTime) AS starttime,
245 Client.Name AS clientname,
246 $jobt.Name AS jobname,
247 $jobt.JobFiles AS jobfiles
248 FROM $jobt, FileSet, Client $filter $groupf
249 WHERE $jobt.ClientId = Client.ClientId
250 AND $jobt.FileSetId = FileSet.FileSetId
261 print STDERR $query if ($debug);
263 my $obj = get_graph('title' => "Job Files : $arg->{jclients}/$arg->{jjobnames}",
264 'y_label' => 'Number Files',
268 my $all = $dbh->selectall_arrayref($query) ;
270 my ($d, $ret) = make_tab($all);
272 $obj->set_legend(keys %$ret);
274 print $obj->plot([$d, values %$ret])->png;
277 # it works only with postgresql at this time
278 # we dont use $jobt because we use File, so job is in Job table
279 elsif ($graph eq 'file_histo' and $arg->{where}) {
281 my $dir = $dbh->quote(dirname($arg->{where}) . '/');
282 my $file = $dbh->quote(basename($arg->{where}));
285 SELECT UNIX_TIMESTAMP(Job.StartTime) AS starttime,
286 Client.Name AS client,
288 base64_decode_lstat(8,LStat) AS lstat
290 FROM Job, FileSet, Filename, Path, File, Client $filter
291 WHERE Job.ClientId = Client.ClientId
292 AND Job.FileSetId = FileSet.FileSetId
294 AND File.JobId = Job.JobId
295 AND File.FilenameId = Filename.FilenameId
296 AND File.PathId = Path.PathId
298 AND Filename.Name = $file
307 print STDERR $query if ($debug);
309 my $all = $dbh->selectall_arrayref($query) ;
311 my $obj = get_graph('title' => "File size : $arg->{where}",
312 'y_label' => 'File size',
315 'y_number_format' => \&Bweb::human_size,
319 my ($d, $ret) = make_tab($all);
321 $obj->set_legend(keys %$ret);
323 print $obj->plot([$d, values %$ret])->png;
326 # it works only with postgresql at this time
327 # TODO: use brestore_missing_path
328 elsif ($graph eq 'rep_histo' and $arg->{where}) {
330 my $dir = $arg->{where};
331 $dir .= '/' if ($dir !~ m!/$!);
332 $dir = $dbh->quote($dir);
335 SELECT UNIX_TIMESTAMP(Job.StartTime) AS starttime,
336 Client.Name AS client,
338 brestore_pathvisibility.size AS size
340 FROM Job, Client $filter, FileSet, Path, brestore_pathvisibility
341 WHERE Job.ClientId = Client.ClientId
342 AND Job.FileSetId = FileSet.FileSetId
344 AND Job.JobId = brestore_pathvisibility.JobId
345 AND Path.PathId = brestore_pathvisibility.PathId
355 print STDERR $query if ($debug);
357 my $all = $dbh->selectall_arrayref($query) ;
359 my $obj = get_graph('title' => "Directory size : $arg->{where}",
360 'y_label' => 'Directory size',
363 'y_number_format' => \&Bweb::human_size,
367 my ($d, $ret) = make_tab($all);
369 $obj->set_legend(keys %$ret);
371 print $obj->plot([$d, values %$ret])->png;
374 elsif ($graph eq 'job_rate') {
378 UNIX_TIMESTAMP($jobt.StartTime) AS starttime,
379 Client.Name AS clientname,
380 $jobt.Name AS jobname,
382 ($bweb->{sql}->{SEC_TO_INT}(
383 $bweb->{sql}->{UNIX_TIMESTAMP}(EndTime)
384 - $bweb->{sql}->{UNIX_TIMESTAMP}(StartTime)) + 0.01)
387 FROM $jobt, FileSet, Client $filter $groupf
388 WHERE $jobt.ClientId = Client.ClientId
389 AND $jobt.FileSetId = FileSet.FileSetId
400 print STDERR $query if ($debug);
402 my $obj = get_graph('title' => "Job Rate : $arg->{jclients}/$arg->{jjobnames}",
403 'y_label' => 'Rate b/s',
405 'y_number_format' => \&Bweb::human_size,
408 my $all = $dbh->selectall_arrayref($query) ;
410 my ($d, $ret) = make_tab($all);
412 $obj->set_legend(keys %$ret);
414 print $obj->plot([$d, values %$ret])->png;
419 elsif ($graph eq 'job_duration') {
423 UNIX_TIMESTAMP($jobt.StartTime) AS starttime,
424 Client.Name AS clientname,
425 $jobt.Name AS jobname,
426 $bweb->{sql}->{SEC_TO_INT}( $bweb->{sql}->{UNIX_TIMESTAMP}(EndTime)
427 - $bweb->{sql}->{UNIX_TIMESTAMP}(StartTime))
429 FROM $jobt, FileSet, Client $filter $groupf
430 WHERE $jobt.ClientId = Client.ClientId
431 AND $jobt.FileSetId = FileSet.FileSetId
442 print STDERR $query if ($debug);
444 my $obj = get_graph('title' => "Job Duration : $arg->{jclients}/$arg->{jjobnames}",
445 'y_label' => 'Duration',
447 'y_number_format' => \&Bweb::human_sec,
449 my $all = $dbh->selectall_arrayref($query) ;
451 my ($d, $ret) = make_tab($all);
453 $obj->set_legend(keys %$ret);
455 print $obj->plot([$d, values %$ret])->png;
458 # number of job per day/hour
459 } elsif ($graph =~ /^job_(count|sum|avg)_((p?)(day|hour|month))$/) {
463 my ($limit, $label) = $bweb->get_limit(age => $arg->{age},
464 limit => $arg->{limit},
465 offset=> $arg->{offset},
469 my @arg; # arg for plotting
471 if (!$per_t) { # much better aspect
474 push @arg, ("x_number_format" => undef,
479 if ($t eq 'sum' or $t eq 'avg') {
480 push @arg, ('y_number_format' => \&Bweb::human_size);
483 my $stime = $bweb->{sql}->{"STARTTIME_$d"};
484 $stime =~ s/Job\./$jobt\./;
488 " . ($per_t?"":"UNIX_TIMESTAMP") . "($stime) AS A,
490 FROM $jobt, FileSet, Client $filter $groupf
491 WHERE $jobt.ClientId = Client.ClientId
492 AND $jobt.FileSetId = FileSet.FileSetId
503 print STDERR $query if ($debug);
505 my $obj = get_graph('title' => "Job $t : $arg->{jclients}/$arg->{jjobnames}",
511 my $all = $dbh->selectall_arrayref($query) ;
512 # print STDERR Data::Dumper::Dumper($all);
513 my ($ret) = make_tab_sum($all);
515 print $obj->plot([$ret->{date}, $ret->{nb}])->png;