=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-2007 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
=head2 EXAMPLE
- $ref = { name => 'me', age => 26 };
+ $ref = { name => 'me', age => 26Â };
$self->display($ref, "people.tpl");
=cut
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\.-]+)$/,
bconsole => qr!^(.+)?$!,
syslog_file => qr!^(.+)?$!,
log_dir => qr!^(.+)?$!,
+ stat_job_table => qr!^(\w*)$!,
+ display_log_time => qr!^(on)?$!,
+ enable_security => qr/^(on)?$/,
+ enable_security_acl => qr/^(on)?$/,
);
=head1 FUNCTION
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)
my ($self) = @_;
$self->{error} = '';
+ # we need to reset checkbox first
$self->{debug} = 0;
+ $self->{display_log_time} = 0;
+ $self->{enable_security} = 0;
+ $self->{enable_security_acl} = 0;
foreach my $k (CGI::param())
{
sub transfer
{
my ($self, $src, $dst) = @_ ;
- print "<pre>$self->{precmd} $self->{mtxcmd} -f $self->{device} transfer $src $dst</pre>\n";
+ if ($self->{debug}) {
+ print "<pre>$self->{precmd} $self->{mtxcmd} -f $self->{device} transfer $src $dst</pre>\n";
+ }
my $out = `$self->{precmd} $self->{mtxcmd} -f $self->{device} transfer $src $dst 2>&1`;
if ($? == 0) {
my $content = $self->get_slot($src);
- print "$content ($src) => $dst<br/>";
$self->{slot}->[$src] = 'empty';
$self->set_slot($dst, $content);
return 1;
if ($self->slot_is_full($slot))
{
my $free = $self->slot_get_first_free() ;
- print "want to move $slot to $free\n";
+ print "move $slot to $free :\n";
if ($free) {
- $self->transfer($slot, $free) || print "$self->{error}\n";
+ if ($self->transfer($slot, $free)) {
+ print "<img src='/bweb/T.png' alt='ok'><br/>\n";
+ } else {
+ print "<img src='/bweb/E.png' alt='ok' title='$self->{error}'><br/>\n";
+ }
} else {
- $self->{error} = "E : Can't find free slot";
+ $self->{error} = "<img src='/bweb/E.png' alt='ok' title='E : Can t find free slot'><br/>\n";
}
}
}
} else { # empty or no label
push @{ $param }, {realslot => $slot,
- volstatus => 'Unknow',
+ volstatus => 'Unknown',
volumename => $self->{slot}->[$slot]} ;
}
} else { # empty
use DBI;
use POSIX qw/strftime/;
+our $config_file='/etc/bacula/bweb.conf';
+
our $cur_id=0;
=head1 VARIABLE
TO_SEC => " interval '1 second' * ",
SEC_TO_INT => "SEC_TO_INT",
SEC_TO_TIME => '',
- MATCH => " ~ ",
+ MATCH => " ~* ",
STARTTIME_DAY => " date_trunc('day', Job.StartTime) ",
STARTTIME_HOUR => " date_trunc('hour', Job.StartTime) ",
STARTTIME_MONTH => " date_trunc('month', Job.StartTime) ",
STARTTIME_PHOUR=> " date_part('hour', Job.StartTime) ",
STARTTIME_PDAY => " date_part('day', Job.StartTime) ",
STARTTIME_PMONTH => " date_part('month', Job.StartTime) ",
+ STARTTIME_PWEEK => " date_part('week', Job.StartTime) ",
+ DB_SIZE => " SELECT pg_database_size(current_database()) ",
+ CAT_POOL_TYPE => " MediaType || '_' || Pool.Name ",
+ CONCAT_SEP => "",
},
mysql => {
UNIX_TIMESTAMP => 'UNIX_TIMESTAMP',
STARTTIME_PHOUR=> " DATE_FORMAT(StartTime, '%H') ",
STARTTIME_PDAY => " DATE_FORMAT(StartTime, '%d') ",
STARTTIME_PMONTH => " DATE_FORMAT(StartTime, '%m') ",
+ STARTTIME_PWEEK => " DATE_FORMAT(StartTime, '%v') ",
+ # with mysql < 5, you have to play with the ugly SHOW command
+ DB_SIZE => " SELECT 0 ",
+ # works only with mysql 5
+ # DB_SIZE => " SELECT sum(DATA_LENGTH) FROM INFORMATION_SCHEMA.TABLES ",
+ CAT_POOL_TYPE => " CONCAT(MediaType,'_',Pool.Name) ",
+ CONCAT_SEP => " SEPARATOR '' ",
},
);
+sub dbh_is_mysql
+{
+ my ($self) = @_;
+ return $self->{info}->{dbi} =~ /dbi:mysql/i;
+}
+
+sub dbh_disconnect
+{
+ my ($self) = @_;
+ if ($self->{dbh}) {
+ $self->{dbh}->disconnect();
+ undef $self->{dbh};
+ }
+}
+
sub dbh_selectall_arrayref
{
my ($self, $query) = @_;
return $self->{dbh}->selectrow_hashref($query) ;
}
+sub dbh_strcat
+{
+ my ($self, @what) = @_;
+ if ($self->dbh_is_mysql()) {
+ return 'CONCAT(' . join(',', @what) . ')' ;
+ } else {
+ return join(' || ', @what);
+ }
+}
+
+sub dbh_prepare
+{
+ my ($self, $query) = @_;
+ $self->debug($query, up => 1);
+ return $self->{dbh}->prepare($query);
+}
+
# display Mb/Gb/Kb
sub human_size
{
- my @unit = qw(b Kb Mb Gb Tb);
+ my @unit = qw(B KB MB GB TB);
my $val = shift || 0;
my $i=0;
my $format = '%i %s';
return "$val years";
}
+# display Enabled
+sub human_enabled
+{
+ my $val = shift || 0;
+
+ if ($val eq '1' or $val eq "yes") {
+ return "yes";
+ } elsif ($val eq '2' or $val eq "archived") {
+ return "archived";
+ } else {
+ return "no";
+ }
+}
+
+# display Enabled
+sub from_human_enabled
+{
+ my $val = shift || 0;
+
+ if ($val == 1 or $val eq "yes") {
+ return 1;
+ } elsif ($val == 2 or $val eq "archived") {
+ return 2;
+ } else {
+ return 0;
+ }
+}
+
# get Day, Hour, Year
sub from_human_sec
{
my ($self) = @_;
unless ($self->{dbh}) {
+
$self->{dbh} = DBI->connect($self->{info}->{dbi},
$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->dbh_is_mysql()) {
+ $self->{dbh}->do("SET group_concat_max_len=1000000");
+ } else {
+ $self->{dbh}->do("SET datestyle TO 'ISO, YMD'");
+ }
}
}
sub new
{
my ($class, %arg) = @_;
- my $self = bless {
+ my $self = bless ({
dbh => undef, # connect_db();
info => {
dbi => '', # DBI:Pg:database=bacula;host=127.0.0.1
user => 'bacula',
password => 'test',
},
- } ;
+ },$class) ;
map { $self->{lc($_)} = $arg{$_} } keys %arg ;
$self->{sql} = $sql_func{$1};
}
+ $self->{loginname} = CGI::remote_user();
$self->{debug} = $self->{info}->{debug};
$Bweb::Gui::template_dir = $self->{info}->{template_dir};
sub display_clients
{
my ($self) = @_;
+ my $where=''; # by default
- my $where='';
- my $arg = $self->get_form("client", "qre_client");
+ my $arg = $self->get_form("client", "qre_client",
+ "jclient_groups", "qnotingroup");
if ($arg->{qre_client}) {
$where = "WHERE Name $self->{sql}->{MATCH} $arg->{qre_client} ";
} elsif ($arg->{client}) {
$where = "WHERE Name = '$arg->{client}' ";
+ } elsif ($arg->{jclient_groups}) {
+ # $filter could already contains client_group_member
+ $where = "
+ JOIN client_group_member USING (ClientId)
+ JOIN client_group USING (client_group_id)
+ WHERE client_group_name IN ($arg->{jclient_groups}) ";
+ } elsif ($arg->{qnotingroup}) {
+ $where = "
+ WHERE NOT EXISTS
+ (SELECT 1 FROM client_group_member
+ WHERE Client.ClientId = client_group_member.ClientId
+ )
+";
}
my $query = "
AutoPrune AS autoprune,
FileRetention AS fileretention,
JobRetention AS jobretention
-FROM Client
-$where
-";
+FROM Client " . $self->get_client_filter() .
+$where ;
my $all = $self->dbh_selectall_hashref($query, 'name') ;
maxvoljobs => 0,
maxvolbytes => 0,
maxvolfiles => 0,
+ filenameid => 0,
+ pathid => 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,
+ expired => 1,
+ enabled => 1,
+ username => 1,
+ rolename => 1,
+ );
my %opt_p = ( # option with path
fileset=> 1,
mtxcmd => 1,
precmd => 1,
device => 1,
+ where => 1,
);
+ my %opt_r = (regexwhere => 1);
my %opt_d = ( # option with date
voluseduration=> 1,
if ($value =~ /^([\w\d\.-]+)$/) {
$ret{$i} = $1;
}
-
- } elsif ($i =~ /^j(\w+)s$/) { # quote join args
- my @value = CGI::param($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 "'arg1', 'arg2'"
+ 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:\@\-]+)$/) {
$ret{$i} = $1;
}
+ } elsif (exists $opt_r{$i}) {
+ my $value = CGI::param($i) || '';
+ if ($value =~ /^([^'"']+)$/) {
+ $ret{$i} = $1;
+ }
} elsif (exists $opt_d{$i}) {
my $value = CGI::param($i) || '';
if ($value =~ /^\s*(\d+\s+\w+)$/) {
}
}
+ 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 $filter='';
+ if ($what{filter}) {
+ # get security filter only if asked
+ $filter = $self->get_client_filter();
+ }
+
my $query = "
SELECT Client.Name as clientname
-FROM Client
+ FROM Client $filter
";
my $clients = $self->dbh_selectall_hashref($query, 'clientname');
values %$clients] ;
}
+ if ($what{db_client_groups}) {
+ my $filter='';
+ if ($what{filter}) {
+ # get security filter only if asked
+ $filter = $self->get_client_group_filter();
+ }
+
+ my $query = "
+SELECT client_group_name AS name
+ FROM client_group $filter
+";
+
+ my $grps = $self->dbh_selectall_hashref($query, 'name');
+ $ret{db_client_groups} = [sort {$a->{name} cmp $b->{name} }
+ values %$grps] ;
+ }
+
+ if ($what{db_usernames}) {
+ my $query = "
+SELECT username
+ FROM bweb_user
+";
+
+ my $users = $self->dbh_selectall_hashref($query, 'username');
+ $ret{db_usernames} = [sort {$a->{username} cmp $b->{username} }
+ values %$users] ;
+ }
+
+ if ($what{db_roles}) {
+ my $query = "
+SELECT rolename
+ FROM bweb_role
+";
+
+ my $r = $self->dbh_selectall_hashref($query, 'rolename');
+ $ret{db_roles} = [sort {$a->{rolename} cmp $b->{rolename} }
+ values %$r] ;
+ }
+
if ($what{db_mediatypes}) {
my $query = "
SELECT MediaType as mediatype
-FROM MediaType
+ FROM MediaType
";
- my $medias = $self->dbh_selectall_hashref($query, 'mediatype');
+ my $media = $self->dbh_selectall_hashref($query, 'mediatype');
$ret{db_mediatypes} = [sort {$a->{mediatype} cmp $b->{mediatype} }
- values %$medias] ;
+ values %$media] ;
}
if ($what{db_locations}) {
my $query = "
-SELECT Location as location, Cost as cost FROM Location
+SELECT Location as location, Cost as cost
+ FROM Location
";
my $loc = $self->dbh_selectall_hashref($query, 'location');
$ret{db_locations} = [ sort { $a->{location}
if ($what{db_filesets}) {
my $query = "
SELECT FileSet.FileSet AS fileset
-FROM FileSet
+ FROM FileSet
";
my $filesets = $self->dbh_selectall_hashref($query, 'fileset');
}
if ($what{db_jobnames}) {
+ my $filter='';
+ if ($what{filter}) {
+ $filter = " JOIN Client USING (ClientId) " . $self->get_client_filter();
+ }
my $query = "
SELECT DISTINCT Job.Name AS jobname
-FROM Job
+ FROM Job $filter
";
my $jobnames = $self->dbh_selectall_hashref($query, 'jobname');
if ($what{db_devices}) {
my $query = "
SELECT Device.Name AS name
-FROM Device
+ FROM Device
";
my $devices = $self->dbh_selectall_hashref($query, 'name');
my ($self) = @_;
my $fields = $self->get_form(qw/age level status clients filesets
- graph gtype type
- db_clients limit db_filesets width height
+ graph gtype type filter db_clients
+ limit db_filesets width height
qclients qfilesets qjobnames db_jobnames/);
}
-sub display_client_job
-{
- my ($self, %arg) = @_ ;
-
- $arg{order} = ' Job.JobId DESC ';
- my ($limit, $label) = $self->get_limit(%arg);
-
- my $clientname = $self->dbh_quote($arg{clientname});
-
- my $query="
-SELECT DISTINCT Job.JobId AS jobid,
- Job.Name AS jobname,
- FileSet.FileSet AS fileset,
- Level AS level,
- StartTime AS starttime,
- JobFiles AS jobfiles,
- JobBytes AS jobbytes,
- JobStatus AS jobstatus,
- JobErrors AS joberrors
-
- FROM Client,Job,FileSet
- WHERE Client.Name=$clientname
- AND Client.ClientId=Job.ClientId
- AND Job.FileSetId=FileSet.FileSetId
- $limit
-";
-
- my $all = $self->dbh_selectall_hashref($query, 'jobid') ;
-
- $self->display({ clientname => $arg{clientname},
- Filter => $label,
- ID => $cur_id++,
- Jobs => [ values %$all ],
- },
- "display_client_job.tpl") ;
-}
-
sub get_selected_media_location
{
my ($self) = @_ ;
- my $medias = $self->get_form('jmedias');
+ my $media = $self->get_form('jmedias');
- unless ($medias->{jmedias}) {
+ unless ($media->{jmedias}) {
return undef;
}
my $query = "
SELECT Media.VolumeName AS volumename, Location.Location AS location
FROM Media LEFT JOIN Location ON (Media.LocationId = Location.LocationId)
-WHERE Media.VolumeName IN ($medias->{jmedias})
+WHERE Media.VolumeName IN ($media->{jmedias})
";
my $all = $self->dbh_selectall_hashref($query, 'volumename') ;
sub move_media
{
- my ($self) = @_ ;
+ my ($self, $in) = @_ ;
- my $medias = $self->get_selected_media_location();
+ my $media = $self->get_selected_media_location();
- unless ($medias) {
+ unless ($media) {
return ;
}
-
+
my $elt = $self->get_form('db_locations');
$self->display({ ID => $cur_id++,
+ enabled => human_enabled($in),
%$elt, # db_locations
- medias => [
- sort { $a->{volumename} cmp $b->{volumename} } values %$medias
+ media => [
+ sort { $a->{volumename} cmp $b->{volumename} } values %$media
],
},
"move_media.tpl");
my $all = $self->dbh_selectall_hashref($query, 'volumename') ;
- $self->display({ Medias => [ values %$all ] },
+ $self->display({ Media => [ values %$all ] },
"help_extern_compute.tpl");
}
my $all = $self->dbh_selectall_hashref($query, 'volumename') ;
- $self->display({ Medias => [ values %$all ] },
+ $self->display({ Media => [ values %$all ] },
"help_intern_compute.tpl");
}
(SELECT count(Media.MediaId) FROM Media) AS nb_media,
(SELECT count(Job.JobId) FROM Job) AS nb_job,
(SELECT sum(VolBytes) FROM Media) AS nb_bytes,
+ ($self->{sql}->{DB_SIZE}) AS db_size,
(SELECT count(Job.JobId)
FROM Job
WHERE Job.JobStatus IN ('E','e','f','A')
$row->{nb_bytes} = human_size($row->{nb_bytes});
- $row->{db_size} = '???';
+ $row->{db_size} = human_size($row->{db_size});
$row->{label} = $label;
$self->display($row, "general.tpl");
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{client_groups}) {
+ my @clients = grep { ! /^\s*$/ } CGI::param('client_group');
+ if (@clients) {
+ $ret{client_groups} = \@clients;
+ my $str = $self->dbh_join(@clients);
+ $limit .= "AND client_group_name IN ($str) ";
+ }
+ }
+
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');
- if (@medias) {
- $ret{mediatypes} = \@medias;
- my $str = $self->dbh_join(@medias);
+ my @media = grep { ! /^\s*$/ } CGI::param('mediatype');
+ if (@media) {
+ $ret{mediatypes} = \@media;
+ my $str = $self->dbh_join(@media);
$limit .= "AND Media.MediaType IN ($str) ";
}
}
$ret{status} = $1;
if ($1 eq 'f') {
$limit .= "AND Job.JobStatus IN ('f','E') ";
- } else {
+ } elsif ($1 eq 'W') {
+ $limit .= "AND Job.JobStatus = 'T' AND Job.JobErrors > 0 ";
+ } else {
$limit .= "AND Job.JobStatus = '$1' ";
}
}
}
+ 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);
sub display_job
{
my ($self, %arg) = @_ ;
+ return if $self->cant_do('r_view_job');
$arg{order} = ' Job.JobId DESC ';
my ($limit, $label) = $self->get_limit(%arg);
my ($where, undef) = $self->get_param('clients',
+ 'client_groups',
'level',
'filesets',
'jobtype',
+ 'pools',
'jobid',
'status');
+ my $cgq='';
+ if (CGI::param('client_group')) {
+ $cgq .= "
+JOIN client_group_member USING (ClientId)
+JOIN client_group USING (client_group_id)
+";
+ }
+ my $filter = $self->get_client_filter();
my $query="
SELECT Job.JobId AS jobid,
Job.Name AS jobname,
Level AS level,
StartTime AS starttime,
+ EndTime AS endtime,
Pool.Name AS poolname,
JobFiles AS jobfiles,
JobBytes AS jobbytes,
JobErrors AS joberrors
- FROM Client,
+ FROM Client $filter $cgq,
Job LEFT JOIN Pool ON (Job.PoolId = Pool.PoolId)
LEFT JOIN FileSet ON (Job.FileSetId = FileSet.FileSetId)
WHERE Client.ClientId=Job.ClientId
- AND Job.JobStatus != 'R'
+ AND Job.JobStatus NOT IN ('R', 'C')
$where
$limit
";
sub display_job_zoom
{
my ($self, $jobid) = @_ ;
+ $self->can_do('r_view_job');
$jobid = $self->dbh_quote($jobid);
-
+
+ # get security filter
+ my $filter = $self->get_client_filter();
+
my $query="
SELECT DISTINCT Job.JobId AS jobid,
Client.Name AS client,
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
- FROM Client,
+ FROM Client $filter,
Job LEFT JOIN FileSet ON (Job.FileSetId = FileSet.FileSetId)
LEFT JOIN Pool ON (Job.PoolId = Pool.PoolId)
WHERE Client.ClientId=Job.ClientId
$self->display($row, "display_job_zoom.tpl");
}
+sub display_job_group
+{
+ my ($self, %arg) = @_;
+ $self->can_do('r_view_job');
+
+ my ($limit, $label) = $self->get_limit(groupby => 'client_group_name', %arg);
+
+ my ($where, undef) = $self->get_param('client_groups',
+ 'level',
+ 'pools');
+ my $filter = $self->get_client_group_filter();
+ my $query =
+"
+SELECT client_group_name AS client_group_name,
+ COALESCE(jobok.jobfiles,0) + COALESCE(joberr.jobfiles,0) AS jobfiles,
+ COALESCE(jobok.jobbytes,0) + COALESCE(joberr.jobbytes,0) AS jobbytes,
+ COALESCE(jobok.joberrors,0) + COALESCE(joberr.joberrors,0) AS joberrors,
+ COALESCE(jobok.nbjobs,0) AS nbjobok,
+ COALESCE(joberr.nbjobs,0) AS nbjoberr,
+ COALESCE(jobok.duration, '0:0:0') AS duration
+
+FROM client_group $filter LEFT JOIN (
+ SELECT client_group_name AS client_group_name, COUNT(1) AS nbjobs,
+ SUM(JobFiles) AS jobfiles, SUM(JobBytes) AS jobbytes,
+ SUM(JobErrors) AS joberrors,
+ SUM($self->{sql}->{SEC_TO_TIME}( $self->{sql}->{UNIX_TIMESTAMP}(EndTime)
+ - $self->{sql}->{UNIX_TIMESTAMP}(StartTime)))
+ AS duration
+
+ FROM Job JOIN client_group_member ON (Job.ClientId = client_group_member.ClientId)
+ JOIN client_group USING (client_group_id)
+
+ WHERE JobStatus = 'T'
+ $where
+ $limit
+) AS jobok USING (client_group_name) LEFT JOIN
+
+(
+ SELECT client_group_name AS client_group_name, COUNT(1) AS nbjobs,
+ SUM(JobFiles) AS jobfiles, SUM(JobBytes) AS jobbytes,
+ SUM(JobErrors) AS joberrors
+ FROM Job JOIN client_group_member ON (Job.ClientId = client_group_member.ClientId)
+ JOIN client_group USING (client_group_id)
+
+ WHERE JobStatus IN ('f','E', 'A')
+ $where
+ $limit
+) AS joberr USING (client_group_name)
+
+ ";
+
+ my $all = $self->dbh_selectall_hashref($query, 'client_group_name');
+
+ my $rep = { groups => [ values %$all ], age => $arg{age}, filter => $label };
+
+ $self->debug($rep);
+ $self->display($rep, "display_job_group.tpl");
+}
+
sub display_media
{
- my ($self) = @_ ;
+ my ($self, %arg) = @_ ;
- my ($where, %elt) = $self->get_param('pool',
- 'location');
+ my ($limit, $label) = $self->get_limit(%arg);
+ my ($where, %elt) = $self->get_param('pools',
+ 'mediatypes',
+ 'volstatus',
+ 'locations');
- my $arg = $self->get_form('jmedias', 'qre_media');
+ my $arg = $self->get_form('jmedias', 'qre_media', 'expired');
if ($arg->{jmedias}) {
$where = "AND Media.VolumeName IN ($arg->{jmedias}) $where";
if ($arg->{qre_media}) {
$where = "AND Media.VolumeName $self->{sql}->{MATCH} $arg->{qre_media} $where";
}
+ if ($arg->{expired}) {
+ $where = "
+ AND VolStatus = 'Full'
+ AND ( $self->{sql}->{UNIX_TIMESTAMP}(Media.LastWritten)
+ + $self->{sql}->{TO_SEC}(Media.VolRetention)
+ ) < NOW() " . $where ;
+ }
my $query="
SELECT Media.VolumeName AS volumename,
WHERE Media.PoolId=Pool.PoolId
$where
+$limit
";
my $all = $self->dbh_selectall_hashref($query, 'volumename') ;
$self->display({ ID => $cur_id++,
Pool => $elt{pool},
Location => $elt{location},
- Medias => [ values %$all ]
+ Media => [ values %$all ],
},
"display_media.tpl");
}
-sub display_medias
+sub display_allmedia
{
my ($self) = @_ ;
{
my ($self) = @_ ;
- my $medias = $self->get_form('jmedias');
+ my $media = $self->get_form('jmedias');
- unless ($medias->{jmedias}) {
+ unless ($media->{jmedias}) {
return $self->error("Can't get media selection");
}
my $query="
SELECT InChanger AS online,
+ Media.Enabled AS enabled,
VolBytes AS nb_bytes,
VolumeName AS volumename,
VolStatus AS volstatus,
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)
FROM Pool,
Media LEFT JOIN Location ON (Media.LocationId = Location.LocationId)
WHERE Pool.PoolId = Media.PoolId
- AND VolumeName IN ($medias->{jmedias})
+ AND VolumeName IN ($media->{jmedias})
";
my $all = $self->dbh_selectall_hashref($query, 'volumename') ;
sub location_edit
{
my ($self) = @_ ;
+ $self->can_do('r_location_mgnt');
my $loc = $self->get_form('qlocation');
unless ($loc->{qlocation}) {
";
my $row = $self->dbh_selectrow_hashref($query);
-
+ $row->{enabled} = human_enabled($row->{enabled});
$self->display({ ID => $cur_id++,
%$row }, "location_edit.tpl") ;
-
}
sub location_save
{
my ($self) = @_ ;
+ $self->can_do('r_location_mgnt');
- my $arg = $self->get_form(qw/qlocation qnewlocation cost/) ;
+ my $arg = $self->get_form(qw/qlocation qnewlocation cost enabled/) ;
unless ($arg->{qlocation}) {
return $self->error("Can't get location");
}
return $self->error("Can't get new cost");
}
- my $enabled = CGI::param('enabled') || '';
- $enabled = $enabled?1:0;
+ my $enabled = from_human_enabled($arg->{enabled});
my $query = "
UPDATE Location SET Cost = $arg->{cost},
$self->dbh_do($query);
- $self->display_location();
+ $self->location_display();
}
sub location_del
{
my ($self) = @_ ;
+ $self->can_do('r_location_mgnt');
+
my $arg = $self->get_form(qw/qlocation/) ;
unless ($arg->{qlocation}) {
}
$query = "
-DELETE FROM Location WHERE Location = $arg->{qlocation} LIMIT 1
+DELETE FROM Location WHERE Location = $arg->{qlocation}
";
$self->dbh_do($query);
- $self->display_location();
+ $self->location_display();
}
-
sub location_add
{
my ($self) = @_ ;
+ $self->can_do('r_location_mgnt');
+
my $arg = $self->get_form(qw/qlocation cost/) ;
unless ($arg->{qlocation}) {
$self->dbh_do($query);
- $self->display_location();
+ $self->location_display();
}
-sub display_location
+sub location_display
{
my ($self) = @_ ;
{
my ($self) = @_ ;
- my $medias = $self->get_selected_media_location();
- unless ($medias) {
+ my $media = $self->get_selected_media_location();
+ unless ($media) {
return ;
}
$self->display({ email => $self->{info}->{email_media},
%$arg,
- medias => [ values %$medias ],
+ media => [ values %$media ],
},
"update_location.tpl");
}
+###########################################################
+
+sub groups_edit
+{
+ my ($self) = @_;
+ $self->can_do('r_group_mgnt');
+
+ my $grp = $self->get_form(qw/qclient_group db_clients/);
+
+ unless ($grp->{qclient_group}) {
+ $self->display({ ID => $cur_id++,
+ client_group => "''",
+ %$grp,
+ }, "groups_edit.tpl");
+ return;
+ }
+
+ my $query = "
+SELECT Name AS name
+ FROM Client JOIN client_group_member using (clientid)
+ JOIN client_group using (client_group_id)
+WHERE client_group_name = $grp->{qclient_group}
+";
+
+ my $row = $self->dbh_selectall_hashref($query, "name");
+ $self->debug($row);
+ $self->display({ ID => $cur_id++,
+ client_group => $grp->{qclient_group},
+ %$grp,
+ client_group_member => [ values %$row]},
+ "groups_edit.tpl");
+}
+
+sub groups_save
+{
+ my ($self) = @_;
+ $self->can_do('r_group_mgnt');
+
+ my $arg = $self->get_form(qw/qclient_group jclients qnewgroup/);
+
+ if (!$arg->{qclient_group} and $arg->{qnewgroup}) {
+ my $query = "
+INSERT INTO client_group (client_group_name)
+VALUES ($arg->{qnewgroup})
+";
+ $self->dbh_do($query);
+ $arg->{qclient_group} = $arg->{qnewgroup};
+ }
+
+ unless ($arg->{qclient_group}) {
+ return $self->error("Can't get groups");
+ }
+
+ $self->{dbh}->begin_work();
+
+ my $query = "
+DELETE FROM client_group_member
+ WHERE client_group_id IN
+ (SELECT client_group_id
+ FROM client_group
+ WHERE client_group_name = $arg->{qclient_group})
+";
+ $self->dbh_do($query);
+
+ $query = "
+ INSERT INTO client_group_member (clientid, client_group_id)
+ (SELECT Clientid,
+ (SELECT client_group_id
+ FROM client_group
+ WHERE client_group_name = $arg->{qclient_group})
+ FROM Client WHERE Name IN ($arg->{jclients})
+ )
+";
+ $self->dbh_do($query);
+
+ if ($arg->{qclient_group} ne $arg->{qnewgroup}) {
+ $query = "
+UPDATE client_group
+ SET client_group_name = $arg->{qnewgroup}
+ WHERE client_group_name = $arg->{qclient_group}
+";
+
+ $self->dbh_do($query);
+ }
+
+ $self->{dbh}->commit() or $self->error($self->{dbh}->errstr);
+
+ $self->display_groups();
+}
+
+sub groups_del
+{
+ my ($self) = @_;
+ $self->can_do('r_group_mgnt');
+
+ my $arg = $self->get_form(qw/qclient_group/);
+
+ unless ($arg->{qclient_group}) {
+ return $self->error("Can't get groups");
+ }
+
+ $self->{dbh}->begin_work();
+
+ my $query = "
+DELETE FROM client_group_member
+ WHERE client_group_id IN
+ (SELECT client_group_id
+ FROM client_group
+ WHERE client_group_name = $arg->{qclient_group});
+
+DELETE FROM bweb_client_group_acl
+ WHERE client_group_id IN
+ (SELECT client_group_id
+ FROM client_group
+ WHERE client_group_name = $arg->{qclient_group});
+
+DELETE FROM client_group
+ WHERE client_group_name = $arg->{qclient_group};
+";
+ $self->dbh_do($query);
+
+ $self->{dbh}->commit();
+
+ $self->display_groups();
+}
+
+sub display_groups
+{
+ my ($self) = @_;
+
+ my $arg = $self->get_form(qw/db_client_groups/) ;
+
+ if ($self->{dbh}->errstr) {
+ return $self->error("Can't use groups with bweb, read INSTALL to enable them");
+ }
+
+ $self->debug($arg);
+
+ $self->display({ ID => $cur_id++,
+ %$arg},
+ "display_groups.tpl");
+}
+
+###########################################################
+
+sub get_roles
+{
+ my ($self) = @_;
+ if (not $self->{info}->{enable_security}) {
+ return 1;
+ }
+ # admin is a special user that can do everything
+ if ($self->{loginname} eq 'admin') {
+ return 1;
+ }
+ if (!$self->{loginname}) {
+ return 0;
+ }
+ # already fill
+ if (defined $self->{security}) {
+ return 1;
+ }
+ $self->{security} = {};
+ my $u = $self->dbh_quote($self->{loginname});
+
+ my $query = "
+ SELECT use_acl, rolename
+ FROM bweb_user
+ JOIN bweb_role_member USING (userid)
+ JOIN bweb_role USING (roleid)
+ WHERE username = $u
+";
+ my $rows = $self->dbh_selectall_arrayref($query);
+ # do cache with this role
+ if (!$rows) {
+ return 0;
+ }
+ foreach my $r (@$rows) {
+ $self->{security}->{$r->[1]}=1;
+ }
+
+ $self->{security}->{use_acl} = $rows->[0]->[0];
+ return 1;
+}
+
+sub cant_do
+{
+ my ($self, $action) = @_;
+ # is security enabled in configuration ?
+ if (not $self->{info}->{enable_security}) {
+ return 0
+ }
+ # admin is a special user that can do everything
+ if ($self->{loginname} eq 'admin') {
+ return 0;
+ }
+ # must be logged
+ if (!$self->{loginname}) {
+ $self->{error} = "Can't do $action, your are not logged. " .
+ "Check security with your administrator";
+ return 1;
+ }
+ $self->get_roles();
+ if (!$self->{security}->{$action}) {
+ $self->{error} =
+ "$self->{loginname} sorry, but this action ($action) " .
+ "is not permited. " .
+ "Check security with your administrator";
+ return 1;
+ }
+ return 0;
+}
+
+# make like an assert (program die)
+sub can_do
+{
+ my ($self, $action) = @_;
+ if ($self->cant_do($action)) {
+ $self->error($self->{error});
+ $self->display_end();
+ exit 0;
+ }
+ return 1;
+}
+
+sub use_filter
+{
+ my ($self) = @_;
+
+ if (!$self->{info}->{enable_security} or
+ !$self->{info}->{enable_security_acl})
+ {
+ return 0 ;
+ }
+
+ if ($self->get_roles()) {
+ return $self->{security}->{use_acl};
+ } else {
+ return 0;
+ }
+}
+
+# JOIN Client USING (ClientId) " . $b->get_client_filter() . "
+sub get_client_filter
+{
+ my ($self, $login) = @_;
+ my $u;
+ if ($login) {
+ $u = $self->dbh_quote($login);
+ } elsif ($self->use_filter()) {
+ $u = $self->dbh_quote($self->{loginname});
+ } else {
+ return '';
+ }
+ return "
+ JOIN (SELECT ClientId FROM client_group_member
+ JOIN client_group USING (client_group_id)
+ JOIN bweb_client_group_acl USING (client_group_id)
+ JOIN bweb_user USING (userid)
+ WHERE bweb_user.username = $u
+ ) AS filter USING (ClientId)";
+}
+
+#JOIN client_group USING (client_group_id)" . $b->get_client_group_filter()
+sub get_client_group_filter
+{
+ my ($self, $login) = @_;
+ my $u;
+ if ($login) {
+ $u = $self->dbh_quote($login);
+ } elsif ($self->use_filter()) {
+ $u = $self->dbh_quote($self->{loginname});
+ } else {
+ return '';
+ }
+ return "
+ JOIN (SELECT client_group_id
+ FROM bweb_client_group_acl
+ JOIN bweb_user USING (userid)
+ WHERE bweb_user.username = $u
+ ) AS filter USING (client_group_id)";
+}
+
+# role and username have to be quoted before
+# role and username can be a quoted list
+sub revoke
+{
+ my ($self, $role, $username) = @_;
+ $self->can_do("r_user_mgnt");
+
+ my $nb = $self->dbh_do("
+ DELETE FROM bweb_role_member
+ WHERE roleid = (SELECT roleid FROM bweb_role
+ WHERE rolename IN ($role))
+ AND userid = (SELECT userid FROM bweb_user
+ WHERE username IN ($username))");
+ return $nb;
+}
+
+# role and username have to be quoted before
+# role and username can be a quoted list
+sub grant
+{
+ my ($self, $role, $username) = @_;
+ $self->can_do("r_user_mgnt");
+
+ my $nb = $self->dbh_do("
+ INSERT INTO bweb_role_member (roleid, userid)
+ SELECT roleid, userid FROM bweb_role, bweb_user
+ WHERE rolename IN ($role)
+ AND username IN ($username)
+ ");
+ return $nb;
+}
+
+# role and username have to be quoted before
+# role and username can be a quoted list
+sub grant_like
+{
+ my ($self, $copy, $user) = @_;
+ $self->can_do("r_user_mgnt");
+
+ my $nb = $self->dbh_do("
+ INSERT INTO bweb_role_member (roleid, userid)
+ SELECT roleid, a.userid
+ FROM bweb_user AS a, bweb_role_member
+ JOIN bweb_user USING (userid)
+ WHERE bweb_user.username = $copy
+ AND a.username = $user");
+ return $nb;
+}
+
+# username can be a join quoted list of usernames
+sub revoke_all
+{
+ my ($self, $username) = @_;
+ $self->can_do("r_user_mgnt");
+
+ $self->dbh_do("
+ DELETE FROM bweb_role_member
+ WHERE userid IN (
+ SELECT userid
+ FROM bweb_user
+ WHERE username in ($username))");
+ $self->dbh_do("
+DELETE FROM bweb_client_group_acl
+ WHERE userid IN (
+ SELECT userid
+ FROM bweb_user
+ WHERE username IN ($username))");
+
+}
+
+sub users_del
+{
+ my ($self) = @_;
+ $self->can_do("r_user_mgnt");
+
+ my $arg = $self->get_form(qw/jusernames/);
+
+ unless ($arg->{jusernames}) {
+ return $self->error("Can't get user");
+ }
+
+ $self->{dbh}->begin_work();
+ {
+ $self->revoke_all($arg->{jusernames});
+ $self->dbh_do("
+DELETE FROM bweb_user WHERE username IN ($arg->{jusernames})");
+ }
+ $self->{dbh}->commit();
+
+ $self->display_users();
+}
+
+sub users_add
+{
+ my ($self) = @_;
+ $self->can_do("r_user_mgnt");
+
+ # we don't quote username directly to check that it is conform
+ my $arg = $self->get_form(qw/username qpasswd qcomment jrolenames qcreate qcopy_username jclient_groups/) ;
+
+ if (not $arg->{qcreate}) {
+ $arg = $self->get_form(qw/db_roles db_usernames db_client_groups/);
+ $self->display($arg, "display_user.tpl");
+ return 1;
+ }
+
+ my $u = $self->dbh_quote($arg->{username});
+
+ $arg->{use_acl}=(CGI::param('use_acl')?'true':'false');
+
+ if (!$arg->{qpasswd}) {
+ $arg->{qpasswd} = "''";
+ }
+ if (!$arg->{qcomment}) {
+ $arg->{qcomment} = "''";
+ }
+
+ # will fail if user already exists
+ # UPDATE with mysql dbi does not return if update is ok
+ ($self->dbh_do("
+ UPDATE bweb_user
+ SET passwd=$arg->{qpasswd}, comment=$arg->{qcomment},
+ use_acl=$arg->{use_acl}
+ WHERE username = $u")
+# and (! $self->dbh_is_mysql() )
+ ) and
+ $self->dbh_do("
+ INSERT INTO bweb_user (username, passwd, use_acl, comment)
+ VALUES ($u, $arg->{qpasswd}, $arg->{use_acl}, $arg->{qcomment})");
+
+ $self->{dbh}->begin_work();
+ {
+ $self->revoke_all($u);
+
+ if ($arg->{qcopy_username}) {
+ $self->grant_like($arg->{qcopy_username}, $u);
+ } else {
+ $self->grant($arg->{jrolenames}, $u);
+ }
+
+ if ($arg->{jclient_groups}) {
+ $self->dbh_do("
+INSERT INTO bweb_client_group_acl (client_group_id, userid)
+ SELECT client_group_id, userid
+ FROM client_group, bweb_user
+ WHERE client_group_name IN ($arg->{jclient_groups})
+ AND username = $u
+");
+ }
+ }
+ $self->{dbh}->commit();
+
+ $self->display_users();
+}
+
+# TODO: we miss a matrix with all user/roles
+sub display_users
+{
+ my ($self) = @_;
+ $self->can_do("r_user_mgnt");
+
+ my $arg = $self->get_form(qw/db_usernames/) ;
+
+ if ($self->{dbh}->errstr) {
+ return $self->error("Can't use users with bweb, read INSTALL to enable them");
+ }
+
+ $self->display({ ID => $cur_id++,
+ %$arg},
+ "display_users.tpl");
+}
+
+sub display_user
+{
+ my ($self) = @_;
+ $self->can_do("r_user_mgnt");
+
+ my $arg = $self->get_form('username');
+ my $user = $self->dbh_quote($arg->{username});
+
+ my $userp = $self->dbh_selectrow_hashref("
+ SELECT username, passwd, comment, use_acl
+ FROM bweb_user
+ WHERE username = $user
+");
+ if (!$userp) {
+ return $self->error("Can't find $user in catalog");
+ }
+ my $filter = $self->get_client_group_filter($arg->{username});
+ my $scg = $self->dbh_selectall_hashref("
+ SELECT client_group_name AS name
+ FROM client_group $filter
+", 'name');
+
+# rolename | userid
+#------------+--------
+# cancel_job |
+# restore |
+# run_job | 1
+
+ my $role = $self->dbh_selectall_hashref("
+SELECT rolename, temp.userid
+ FROM bweb_role
+ LEFT JOIN (SELECT roleid, userid
+ FROM bweb_user JOIN bweb_role_member USING (userid)
+ WHERE username = $user) AS temp USING (roleid)
+ORDER BY rolename
+", 'rolename');
+
+ $arg = $self->get_form(qw/db_usernames db_client_groups/);
+
+ $self->display({
+ db_usernames => $arg->{db_usernames},
+ username => $userp->{username},
+ comment => $userp->{comment},
+ passwd => $userp->{passwd},
+ use_acl => $userp->{use_acl},
+ db_client_groups => $arg->{db_client_groups},
+ client_group => [ values %$scg ],
+ db_roles => [ values %$role],
+ }, "display_user.tpl");
+}
+
+
+###########################################################
+
sub get_media_max_size
{
my ($self, $type) = @_;
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,
+ Media.Enabled AS enabled
-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}
my $row = $self->dbh_selectrow_hashref($query);
$row->{volretention} = human_sec($row->{volretention});
$row->{voluseduration} = human_sec($row->{voluseduration});
+ $row->{enabled} = human_enabled($row->{enabled});
my $elt = $self->get_form(qw/db_pools db_locations/);
sub save_location
{
my ($self) = @_ ;
+ $self->can_do('r_media_mgnt');
my $arg = $self->get_form('jmedias', 'qnewlocation') ;
$self->display_media();
}
-sub change_location
+sub location_change
{
my ($self) = @_ ;
+ $self->can_do('r_media_mgnt');
- my $medias = $self->get_selected_media_location();
- unless ($medias) {
+ my $media = $self->get_selected_media_location();
+ unless ($media) {
return $self->error("Can't get media selection");
}
my $newloc = CGI::param('newlocation');
- my $user = CGI::param('user') || 'unknow';
+ my $user = CGI::param('user') || 'unknown';
my $comm = CGI::param('comment') || '';
$comm = $self->dbh_quote("$user: $comm");
- my $query;
+ my $arg = $self->get_form('enabled');
+ my $en = human_enabled($arg->{enabled});
+ my $b = $self->get_bconsole();
- foreach my $media (keys %$medias) {
+ my $query;
+ foreach my $vol (keys %$media) {
$query = "
INSERT LocationLog (Date, Comment, MediaId, LocationId, NewVolStatus)
VALUES(
- NOW(), $comm, (SELECT MediaId FROM Media WHERE VolumeName = '$media'),
- (SELECT LocationId FROM Location WHERE Location = '$medias->{$media}->{location}'),
- (SELECT VolStatus FROM Media WHERE VolumeName = '$media')
+ NOW(), $comm, (SELECT MediaId FROM Media WHERE VolumeName = '$vol'),
+ (SELECT LocationId FROM Location WHERE Location = '$media->{$vol}->{location}'),
+ (SELECT VolStatus FROM Media WHERE VolumeName = '$vol')
)
";
$self->dbh_do($query);
$self->debug($query);
+ $b->send_cmd("update volume=\"$vol\" enabled=$en");
}
+ $b->close();
my $q = new CGI;
$q->param('action', 'update_location');
$self->display({ email => $self->{info}->{email_media},
url => $url,
newlocation => $newloc,
- # [ { volumename => 'vol1' }, { volumename => 'vol2' },..]
- medias => [ values %$medias ],
+ # [ { volumename => 'vol1' }, { volumename => 'vol2'},..]
+ media => [ values %$media ],
},
"change_location.tpl");
sub display_client_stats
{
my ($self, %arg) = @_ ;
+ $self->can_do('r_view_stat');
my $client = $self->dbh_quote($arg{clientname});
- my ($limit, $label) = $self->get_limit(%arg);
+ # get security filter
+ my $filter = $self->get_client_filter();
+ my ($limit, $label) = $self->get_limit(%arg);
my $query = "
SELECT
count(Job.JobId) AS nb_jobs,
sum(Job.JobErrors) AS nb_err,
sum(Job.JobFiles) AS nb_files,
Client.Name AS clientname
-FROM Job INNER JOIN Client USING (ClientId)
+FROM Job JOIN Client USING (ClientId) $filter
WHERE
Client.Name = $client
$limit
$row->{ID} = $cur_id++;
$row->{label} = $label;
+ $row->{grapharg} = "client";
+
+ $self->display($row, "display_client_stats.tpl");
+}
+
+
+sub display_group_stats
+{
+ my ($self, %arg) = @_ ;
+
+ my $carg = $self->get_form(qw/qclient_group/);
+
+ unless ($carg->{qclient_group}) {
+ return $self->error("Can't get group");
+ }
+
+ my ($limit, $label) = $self->get_limit(%arg);
+
+ my $query = "
+SELECT
+ count(Job.JobId) AS nb_jobs,
+ sum(Job.JobBytes) AS nb_bytes,
+ sum(Job.JobErrors) AS nb_err,
+ sum(Job.JobFiles) AS nb_files,
+ client_group.client_group_name AS clientname
+FROM Job JOIN Client USING (ClientId)
+ JOIN client_group_member ON (Client.ClientId = client_group_member.clientid)
+ JOIN client_group USING (client_group_id)
+WHERE
+ client_group.client_group_name = $carg->{qclient_group}
+ $limit
+GROUP BY client_group.client_group_name
+";
+
+ my $row = $self->dbh_selectrow_hashref($query);
+
+ $row->{ID} = $cur_id++;
+ $row->{label} = $label;
+ $row->{grapharg} = "client_group";
$self->display($row, "display_client_stats.tpl");
}
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
Pool.MaxVolJobs AS maxvoljobs,
Pool.MaxVolFiles AS maxvolfiles,
Pool.MaxVolBytes AS maxvolbytes,
- subq.PoolId AS PoolId
+ subq.PoolId AS PoolId,
+ subq.MediaType AS mediatype,
+ $self->{sql}->{CAT_POOL_TYPE} AS uniq
FROM
(
SELECT COALESCE(media_avg_size.volavg,0) * count(Media.MediaId) AS volmax,
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') ;
+ my $all = $self->dbh_selectall_hashref($query, 'uniq') ;
+
+ $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;
$query = "
SELECT VolStatus AS volstatus, count(MediaId) AS nb
FROM Media
- WHERE PoolId=$p->{poolid}
+ WHERE PoolId=$p->{poolid}
+ AND Media.MediaType = '$p->{mediatype}'
+ $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");
}
sub display_running_job
{
my ($self) = @_;
+ return if $self->cant_do('r_view_running_job');
my $arg = $self->get_form('client', 'jobid');
if (!$arg->{client} and $arg->{jobid}) {
+ # get security filter
+ my $filter = $self->get_client_filter();
my $query = "
SELECT Client.Name AS name
-FROM Job INNER JOIN Client USING (ClientId)
+FROM Job INNER JOIN Client USING (ClientId) $filter
WHERE Job.JobId = $arg->{jobid}
";
sub display_running_jobs
{
my ($self, $display_action) = @_;
-
+ return if $self->cant_do('r_view_running_job');
+
+ # get security filter
+ my $filter = $self->get_client_filter();
+
my $query = "
SELECT Job.JobId AS jobid,
Job.Name AS jobname,
- $self->{sql}->{UNIX_TIMESTAMP}(StartTime))
AS duration,
Client.Name AS clientname
-FROM Job INNER JOIN Client USING (ClientId)
-WHERE JobStatus IN ('C','R','B','e','D','F','S','m','M','s','j','c','d','t','p')
+FROM Job INNER JOIN Client USING (ClientId) $filter
+WHERE
+ JobStatus IN ('C','R','B','e','D','F','S','m','M','s','j','c','d','t','p')
";
my $all = $self->dbh_selectall_hashref($query, 'jobid') ;
"running_job.tpl") ;
}
+# return the autochanger list to update
sub eject_media
{
my ($self) = @_;
+ $self->can_do('r_media_mgnt');
+
+ my %ret;
my $arg = $self->get_form('jmedias');
unless ($arg->{jmedias}) {
foreach my $vol (values %$all) {
my $a = $self->ach_get($vol->{location});
next unless ($a) ;
+ $ret{$vol->{location}} = 1;
unless ($a->{have_status}) {
$a->status();
$a->{have_status} = 1;
}
-
+ # TODO: set enabled
print "eject $vol->{volumename} from $vol->{storage} : ";
if ($a->send_to_io($vol->{slot})) {
- print "ok</br>";
+ print "<img src='/bweb/T.png' alt='ok'><br/>";
} else {
- print "err</br>";
+ print "<img src='/bweb/E.png' alt='err'><br/>";
}
}
+ return keys %ret;
}
sub move_email
return undef;
}
- $a->{bweb} = $self;
+ $a->{bweb} = $self;
+ $a->{debug} = $self->{debug};
return $a;
}
sub ach_register
{
my ($self, $ach) = @_;
+ $self->can_do('r_configure');
$self->{info}->{ach_list}->{$ach->{name}} = $ach;
sub ach_edit
{
my ($self) = @_;
+ $self->can_do('r_configure');
+
my $arg = $self->get_form('ach');
if (!$arg->{ach}
or !$self->{info}->{ach_list}
sub ach_del
{
my ($self) = @_;
+ $self->can_do('r_configure');
+
my $arg = $self->get_form('ach');
if (!$arg->{ach}
sub ach_add
{
my ($self) = @_;
+ $self->can_do('r_configure');
+
my $arg = $self->get_form('ach', 'mtxcmd', 'device', 'precmd');
my $b = $self->get_bconsole();
sub delete
{
my ($self) = @_;
+ $self->can_do('r_delete_job');
+
my $arg = $self->get_form('jobid');
if ($arg->{jobid}) {
sub do_update_media
{
my ($self) = @_ ;
+ $self->can_do('r_media_mgnt');
my $arg = $self->get_form(qw/media volstatus inchanger pool
slot volretention voluseduration
maxvoljobs maxvolfiles maxvolbytes
+ qcomment poolrecycle enabled
/);
unless ($arg->{media}) {
$update .= " slot=0 inchanger=no ";
}
+ if ($arg->{enabled}) {
+ $update .= " enabled=$arg->{enabled} ";
+ }
+
if ($arg->{pool}) {
$update .= " pool=$arg->{pool} " ;
}
- $arg->{volretention} ||= 0 ;
- if ($arg->{volretention}) {
+ if (defined $arg->{volretention}) {
$update .= " volretention=\"$arg->{volretention}\" " ;
}
- $arg->{voluseduration} ||= 0 ;
- if ($arg->{voluseduration}) {
+ if (defined $arg->{voluseduration}) {
$update .= " voluse=\"$arg->{voluseduration}\" " ;
}
- $arg->{maxvoljobs} ||= 0;
- if ($arg->{maxvoljobs}) {
+ if (defined $arg->{maxvoljobs}) {
$update .= " maxvoljobs=$arg->{maxvoljobs} " ;
}
- $arg->{maxvolfiles} ||= 0;
- if ($arg->{maxvolfiles}) {
+ if (defined $arg->{maxvolfiles}) {
$update .= " maxvolfiles=$arg->{maxvolfiles} " ;
}
- $arg->{maxvolbytes} ||= 0;
- if ($arg->{maxvolbytes}) {
+ if (defined $arg->{maxvolbytes}) {
$update .= " maxvolbytes=$arg->{maxvolbytes} " ;
}
+ if (defined $arg->{poolrecycle}) {
+ $update .= " recyclepool=\"$arg->{poolrecycle}\" " ;
+ }
+
my $b = $self->get_bconsole();
$self->display({
}, "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->{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();
}
sub update_slots
{
my ($self) = @_;
+ $self->can_do('r_autochanger_mgnt');
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"
}
sub get_job_log
{
my ($self) = @_;
+ $self->can_do('r_view_log');
- my $arg = $self->get_form('jobid');
+ my $arg = $self->get_form('jobid', 'limit', 'offset');
unless ($arg->{jobid}) {
return $self->error("Can't get jobid");
}
- my $t = CGI::param('time') || '';
+ if ($arg->{limit} == 100) {
+ $arg->{limit} = 1000;
+ }
+ # get security filter
+ my $filter = $self->get_client_filter();
my $query = "
SELECT Job.Name as name, Client.Name as clientname
- FROM Job INNER JOIN Client ON (Job.ClientId = Client.ClientId)
+ FROM Job INNER JOIN Client USING (ClientId) $filter
WHERE JobId = $arg->{jobid}
";
return $self->error("Can't find $arg->{jobid} in catalog");
}
+ # display only Error and Warning messages
+ $filter = '';
+ if (CGI::param('error')) {
+ $filter = " AND LogText $self->{sql}->{MATCH} 'Error|Warning' ";
+ }
+
+ my $logtext;
+ if (CGI::param('time') || $self->{info}->{display_log_time}) {
+ $logtext = $self->dbh_strcat('Time', " ' ' ", 'LogText');
+ } else {
+ $logtext = 'LogText';
+ }
+
$query = "
-SELECT Time AS time, LogText AS log
- FROM Log
- WHERE JobId = $arg->{jobid}
- ORDER BY Time
+SELECT count(1) AS nbline, JobId AS jobid,
+ GROUP_CONCAT($logtext $self->{sql}->{CONCAT_SEP}) AS logtxt
+ FROM (
+ SELECT JobId, Time, LogText
+ 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})
+ ) ) $filter
+ ORDER BY LogId
+ LIMIT $arg->{limit}
+ OFFSET $arg->{offset}
+ ) AS temp
+ GROUP BY JobId
+
";
- my $log = $self->dbh_selectall_arrayref($query);
+
+ my $log = $self->dbh_selectrow_hashref($query);
unless ($log) {
return $self->error("Can't get log for jobid $arg->{jobid}");
}
- my $logtxt;
- if ($t) {
- # log contains \n
- $logtxt = join("", map { ($_->[0] . ' ' . $_->[1]) } @$log ) ;
- } else {
- $logtxt = join("", map { $_->[1] } @$log ) ;
- }
-
- $self->display({ lines=> $logtxt,
+ $self->display({ lines=> $log->{logtxt},
+ nbline => $log->{nbline},
jobid => $arg->{jobid},
name => $row->{name},
client => $row->{clientname},
+ offset => $arg->{offset},
+ limit => $arg->{limit},
}, 'display_log.tpl');
}
-
sub label_barcodes
{
my ($self) = @_ ;
+ $self->can_do('r_autochanger_mgnt');
my $arg = $self->get_form('ach', 'slots', 'drive');
return $self->error("Can't find autochanger name");
}
+ my $a = $self->ach_get($arg->{ach});
+ unless ($a) {
+ return $self->error("Can't find autochanger name in configuration");
+ }
+
+ my $storage = $a->get_drive_name($arg->{drive});
+ unless ($storage) {
+ return $self->error("Can't get your drive name");
+ }
+
my $slots = '';
+ my $slots_sql = '';
my $t = 300 ;
if ($arg->{slots}) {
$slots = join(",", @{ $arg->{slots} });
+ $slots_sql = " AND Slot IN ($slots) ";
$t += 60*scalar( @{ $arg->{slots} }) ;
}
my $b = new Bconsole(pref => $self->{info}, timeout => $t,log_stdout => 1);
print "<h1>This command can take long time, be patient...</h1>";
print "<pre>" ;
- $b->label_barcodes(storage => $arg->{ach},
+ $b->label_barcodes(storage => $storage,
drive => $arg->{drive},
pool => 'Scratch',
slots => $slots) ;
$b->close();
print "</pre>";
+
+ $self->dbh_do("
+ UPDATE Media
+ SET LocationId = (SELECT LocationId
+ FROM Location
+ WHERE Location = '$arg->{ach}')
+
+ WHERE (LocationId = 0 OR LocationId IS NULL)
+ $slots_sql
+");
+
}
sub purge
{
my ($self) = @_;
+ $self->can_do('r_purge');
my @volume = CGI::param('media');
my $b = new Bconsole(pref => $self->{info}, timeout => 60);
- $self->display({
- content => $b->purge_volume(@volume),
- title => "Purge media",
- name => "purge volume=" . join(' volume=', @volume),
- }, "command.tpl");
+ foreach my $v (@volume) {
+ $self->display({
+ content => $b->purge_volume($v),
+ title => "Purge media",
+ name => "purge volume=$v",
+ }, "command.tpl");
+ }
$b->close();
}
sub prune
{
my ($self) = @_;
+ $self->can_do('r_prune');
my @volume = CGI::param('media');
unless (@volume) {
my $b = new Bconsole(pref => $self->{info}, timeout => 60);
- $self->display({
- content => $b->prune_volume(@volume),
- title => "Prune media",
- name => "prune volume=" . join(' volume=', @volume),
- }, "command.tpl");
-
+ foreach my $v (@volume) {
+ $self->display({
+ content => $b->prune_volume($v),
+ title => "Prune volume",
+ name => "prune volume=$v",
+ }, "command.tpl");
+ }
$b->close();
}
sub cancel_job
{
my ($self) = @_;
+ $self->can_do('r_cancel_job');
my $arg = $self->get_form('jobid');
unless ($arg->{jobid}) {
sub enable_disable_job
{
my ($self, $what) = @_ ;
+ $self->can_do('r_run_job');
my $name = CGI::param('job') || '';
unless ($name =~ /^[\w\d\.\-\s]+$/) {
sub run_job_select
{
my ($self) = @_;
+ $self->can_do('r_run_job');
+
my $b = $self->get_bconsole();
my $joblist = [ map { { name => $_ } } $b->list_job() ];
sub run_job_mod
{
my ($self) = @_;
+ $self->can_do('r_run_job');
+
my $b = $self->get_bconsole();
my $job = CGI::param('job') || '';
+ # we take informations from director, and we overwrite with user wish
my $info = $b->send_cmd("show job=\"$job\"");
my $attr = $self->run_parse_job($info);
+
+ my $arg = $self->get_form('pool', 'level', 'client', 'fileset', 'storage');
+ my %job_opt = (%$attr, %$arg);
my $jobs = [ map {{ name => $_ }} $b->list_job() ];
clients => $clients,
filesets => $filesets,
storages => $storages,
- %$attr,
+ %job_opt,
}, "run_job_mod.tpl");
}
sub run_job
{
my ($self) = @_;
+ $self->can_do('r_run_job');
+
my $b = $self->get_bconsole();
my $jobs = [ map {{ name => $_ }} $b->list_job() ];
sub run_job_now
{
my ($self) = @_;
+ $self->can_do('r_run_job');
+
my $b = $self->get_bconsole();
# 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', 'fileset');
my $job = CGI::param('job') || '';
my $storage = CGI::param('storage') || '';
level => $arg->{level},
storage => $storage,
pool => $arg->{pool},
+ fileset => $arg->{fileset},
+ when => $arg->{when},
);
print $jobid, $b->{error};