=head1 LICENSE
- Copyright (C) 2006 Eric Bollengier
- All rights reserved.
+ Bweb - A Bacula web interface
+ Bacula® - The Network Backup Solution
- 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.
+ Copyright (C) 2000-2006 Free Software Foundation Europe e.V.
- 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.
+ 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.
- 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
+ This program is Free Software; you can redistribute it and/or
+ modify it under the terms of version two of the GNU General Public
+ License as published by the Free Software Foundation plus additions
+ that are listed 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
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+
+ Bacula® is a registered trademark of John Walker.
+ The licensor of Bacula is the Free Software Foundation Europe
+ (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zurich,
+ Switzerland, email:ftf@fsfeurope.org.
=head1 VERSION
use HTML::Template;
our $template_dir='/usr/share/bweb/tpl';
-
=head1 FUNCTION
new - creation a of new Bweb object
our %k_re = ( dbi => qr/^(dbi:(Pg|mysql):(?:\w+=[\w\d\.-]+;?)+)$/i,
user => qr/^([\w\d\.-]+)$/i,
password => qr/^(.*)$/i,
+ fv_write_path => qr!^([/\w\d\.-]+)$!,
template_dir => qr!^([/\w\d\.-]+)$!,
debug => qr/^(on)?$/,
email_media => qr/^([\w\d\.-]+@[\d\w\.-]+)$/,
unless (open(FP, $self->{config_file}))
{
- return $self->error("$self->{config_file} : $!");
+ return $self->error("can't load config_file $self->{config_file} : $!");
}
my $f=''; my $tmpbuffer;
while(read FP,$tmpbuffer,4096)
use DBI;
use POSIX qw/strftime/;
+our $config_file='/etc/bacula/bweb.conf';
+
our $cur_id=0;
=head1 VARIABLE
$self->{info}->{user},
$self->{info}->{password});
- print "Can't connect to your database, see error log\n"
+ $self->error("Can't connect to your database:\n$DBI::errstr\n")
unless ($self->{dbh});
$self->{dbh}->{FetchHashKeyName} = 'NAME_lc';
+
+ if ($self->{info}->{dbi} =~ /^dbi:Pg/i) {
+ $self->{dbh}->do("SET datestyle TO 'ISO, YMD'");
+ }
}
}
maxvolfiles => 0,
);
+ my %opt_ss =( # string with space
+ job => 1,
+ storage => 1,
+ );
my %opt_s = ( # default to ''
ach => 1,
status => 1,
graph => 1,
gtype => 1,
type => 1,
+ poolrecycle => 1,
+ replace => 1,
);
my %opt_p = ( # option with path
fileset=> 1,
mtxcmd => 1,
precmd => 1,
device => 1,
+ where => 1,
);
my %opt_d = ( # option with date
if ($value =~ /^([\w\d\.-]+)$/) {
$ret{$i} = $1;
}
-
+ } elsif ($opt_ss{$i}) { # simple string param (with space)
+ my $value = CGI::param($i) || '';
+ if ($value =~ /^([\w\d\.\-\s]+)$/) {
+ $ret{$i} = $1;
+ }
} elsif ($i =~ /^j(\w+)s$/) { # quote join args
- my @value = CGI::param($1) ;
+ my @value = grep { ! /^\s*$/ } CGI::param($1) ;
if (@value) {
$ret{$i} = $self->dbh_join(@value) ;
}
} elsif ($i =~ /^q(\w+)s$/) { #[ 'arg1', 'arg2']
$ret{$i} = [ map { { name => $self->dbh_quote($_) } }
- CGI::param($1) ];
+ grep { ! /^\s*$/ } CGI::param($1) ];
} elsif (exists $opt_p{$i}) {
my $value = CGI::param($i) || '';
if ($value =~ /^([\w\d\.\/\s:\@\-]+)$/) {
}
}
+ if ($what{when}) {
+ my $when = CGI::param('when') || '';
+ if ($when =~ /^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})$/) {
+ $ret{when} = $1;
+ }
+ }
+
if ($what{db_clients}) {
my $query = "
SELECT Client.Name as clientname
my $limit = '';
if ($elt{clients}) {
- my @clients = CGI::param('client');
+ my @clients = grep { ! /^\s*$/ } CGI::param('client');
if (@clients) {
$ret{clients} = \@clients;
my $str = $self->dbh_join(@clients);
}
if ($elt{filesets}) {
- my @filesets = CGI::param('fileset');
+ my @filesets = grep { ! /^\s*$/ } CGI::param('fileset');
if (@filesets) {
$ret{filesets} = \@filesets;
my $str = $self->dbh_join(@filesets);
}
if ($elt{mediatypes}) {
- my @medias = CGI::param('mediatype');
+ my @medias = grep { ! /^\s*$/ } CGI::param('mediatype');
if (@medias) {
$ret{mediatypes} = \@medias;
my $str = $self->dbh_join(@medias);
}
}
+ if ($elt{volstatus}) {
+ my $status = CGI::param('volstatus') || '';
+ if ($status =~ /^(\w+)$/) {
+ $ret{status} = $1;
+ $limit .= "AND Media.VolStatus = '$1' ";
+ }
+ }
+
if ($elt{locations}) {
- my @location = CGI::param('location') ;
+ my @location = grep { ! /^\s*$/ } CGI::param('location') ;
if (@location) {
$ret{locations} = \@location;
my $str = $self->dbh_join(@location);
}
if ($elt{pools}) {
- my @pool = CGI::param('pool') ;
+ my @pool = grep { ! /^\s*$/ } CGI::param('pool') ;
if (@pool) {
$ret{pools} = \@pool;
my $str = $self->dbh_join(@pool);
'level',
'filesets',
'jobtype',
+ 'pools',
'jobid',
'status');
JobFiles AS jobfiles,
JobBytes AS jobbytes,
JobStatus AS jobstatus,
+ JobErrors AS joberrors,
$self->{sql}->{SEC_TO_TIME}( $self->{sql}->{UNIX_TIMESTAMP}(EndTime)
- $self->{sql}->{UNIX_TIMESTAMP}(StartTime)) AS duration
{
my ($self) = @_ ;
- my ($where, %elt) = $self->get_param('pool',
- 'location');
+ my ($where, %elt) = $self->get_param('pools',
+ 'mediatypes',
+ 'volstatus',
+ 'locations');
my $arg = $self->get_form('jmedias', 'qre_media');
Media.LastWritten AS lastwritten,
Media.VolReadTime/1000000 AS volreadtime,
Media.VolWriteTime/1000000 AS volwritetime,
+ Media.RecycleCount AS recyclecount,
+ Media.Comment AS comment,
$self->{sql}->{FROM_UNIXTIME}(
$self->{sql}->{UNIX_TIMESTAMP}(Media.LastWritten)
+ $self->{sql}->{TO_SEC}(Media.VolRetention)
my $query = "
SELECT Media.Slot AS slot,
- Pool.Name AS poolname,
+ PoolMedia.Name AS poolname,
Media.VolStatus AS volstatus,
Media.InChanger AS inchanger,
Location.Location AS location,
Media.MaxVolJobs AS maxvoljobs,
Media.MaxVolFiles AS maxvolfiles,
Media.VolUseDuration AS voluseduration,
- Media.VolRetention AS volretention
+ Media.VolRetention AS volretention,
+ Media.Comment AS comment,
+ PoolRecycle.Name AS poolrecycle
-FROM Media INNER JOIN Pool ON (Media.PoolId = Pool.PoolId)
+FROM Media INNER JOIN Pool AS PoolMedia ON (Media.PoolId = PoolMedia.PoolId)
+ LEFT JOIN Pool AS PoolRecycle ON (Media.RecyclePoolId = PoolRecycle.PoolId)
LEFT JOIN Location ON (Media.LocationId = Location.LocationId)
WHERE Media.VolumeName = $media->{qmedia}
sub display_pool
{
my ($self, $poolname) = @_ ;
+ my $whereA = '';
+ my $whereW = '';
+
+ my $arg = $self->get_form('jmediatypes', 'qmediatypes');
+ if ($arg->{jmediatypes}) {
+ $whereW = "WHERE MediaType IN ($arg->{jmediatypes}) ";
+ $whereA = "AND MediaType IN ($arg->{jmediatypes}) ";
+ }
# TODO : afficher les tailles et les dates
GROUP BY Media.MediaType, Media.PoolId, media_avg_size.volavg
) AS subq
LEFT JOIN Pool ON (Pool.PoolId = subq.PoolId)
+$whereW
";
my $all = $self->dbh_selectall_hashref($query, 'name') ;
+ $query = "
+SELECT Pool.Name AS name,
+ sum(VolBytes) AS size
+FROM Media JOIN Pool ON (Media.PoolId = Pool.PoolId)
+WHERE Media.VolStatus IN ('Recycled', 'Purged')
+ $whereA
+GROUP BY Pool.Name;
+";
+ my $empty = $self->dbh_selectall_hashref($query, 'name');
+
foreach my $p (values %$all) {
- if ($p->{volmax}) {
+ if ($p->{volmax} > 0) { # mysql returns 0.0000
+ # we remove Recycled/Purged media from pool usage
+ if (defined $empty->{$p->{name}}) {
+ $p->{voltotal} -= $empty->{$p->{name}}->{size};
+ }
$p->{poolusage} = sprintf('%.2f', $p->{voltotal} * 100/ $p->{volmax}) ;
} else {
$p->{poolusage} = 0;
SELECT VolStatus AS volstatus, count(MediaId) AS nb
FROM Media
WHERE PoolId=$p->{poolid}
+ $whereA
GROUP BY VolStatus
";
my $content = $self->dbh_selectall_hashref($query, 'volstatus');
$self->debug($all);
$self->display({ ID => $cur_id++,
+ MediaType => $arg->{qmediatypes}, # [ { name => type1 } , { name => type2 } ]
Pools => [ values %$all ]},
"display_pool.tpl");
}
my $arg = $self->get_form(qw/media volstatus inchanger pool
slot volretention voluseduration
maxvoljobs maxvolfiles maxvolbytes
+ qcomment poolrecycle
/);
unless ($arg->{media}) {
}, "command.tpl");
+ my @q;
+ my $media = $self->dbh_quote($arg->{media});
+
my $loc = CGI::param('location') || '';
if ($loc) {
- my $media = $self->dbh_quote($arg->{media});
$loc = $self->dbh_quote($loc); # is checked by db
- my $query = "
+ push @q, "LocationId=(SELECT LocationId FROM Location WHERE Location=$loc)";
+ }
+ if ($arg->{poolrecycle}) {
+ push @q, "RecyclePoolId=(SELECT PoolId FROM Pool WHERE Name='$arg->{poolrecycle}')";
+ }
+ if (!$arg->{qcomment}) {
+ $arg->{qcomment} = "''";
+ }
+ push @q, "Comment=$arg->{qcomment}";
+
+
+ my $query = "
UPDATE Media
- SET LocationId=(SELECT LocationId FROM Location WHERE Location=$loc)
+ SET " . join (',', @q) . "
WHERE Media.VolumeName = $media
";
- $self->dbh_do($query);
- }
+ $self->dbh_do($query);
$self->update_media();
}
my ($self) = @_;
my $ach = CGI::param('ach') ;
- unless ($ach =~ /^([\w\d\.-]+)$/) {
+ $ach = $self->ach_get($ach);
+ unless ($ach) {
return $self->error("Bad autochanger name");
}
print "<pre>";
my $b = new Bconsole(pref => $self->{info},timeout => 60,log_stdout => 1);
- $b->update_slots($ach);
+ $b->update_slots($ach->{name});
print "</pre>\n"
}
}
$query = "
-SELECT Time AS time, LogText AS log
- FROM Log
- WHERE JobId = $arg->{jobid}
- ORDER BY Time
+SELECT Time AS time, LogText AS log
+ FROM Log
+ WHERE Log.JobId = $arg->{jobid}
+ OR (Log.JobId = 0 AND Time >= (SELECT StartTime FROM Job WHERE JobId=$arg->{jobid})
+ AND Time <= (SELECT COALESCE(EndTime,NOW()) FROM Job WHERE JobId=$arg->{jobid})
+ )
+ ORDER BY LogId;
";
+
my $log = $self->dbh_selectall_arrayref($query);
unless ($log) {
return $self->error("Can't get log for jobid $arg->{jobid}");
# TODO: check input (don't use pool, level)
- my $arg = $self->get_form('pool', 'level', 'client', 'priority');
+ my $arg = $self->get_form('pool', 'level', 'client', 'priority', 'when');
my $job = CGI::param('job') || '';
my $storage = CGI::param('storage') || '';
level => $arg->{level},
storage => $storage,
pool => $arg->{pool},
+ when => $arg->{when},
);
print $jobid, $b->{error};