bconsole => qr!^(.+)?$!,
syslog_file => qr!^(.+)?$!,
log_dir => qr!^(.+)?$!,
- ach_list => qr!^(.+)?$!,
);
=head1 FUNCTION
{
my ($self) = @_ ;
+ unless (open(FP, $self->{config_file}))
+ {
+ return $self->error("$self->{config_file} : $!");
+ }
+ my $f=''; my $tmpbuffer;
+ while(read FP,$tmpbuffer,4096)
+ {
+ $f .= $tmpbuffer;
+ }
+ close(FP);
+
+ my $VAR1;
+
+ no strict; # I have no idea of the contents of the file
+ eval "$f" ;
+ use strict;
+
+ if ($f and $@) {
+ $self->load_old();
+ $self->save();
+ return $self->error("If you update from an old bweb install, your must reload this page and if it's fail again, you have to configure bweb again...") ;
+ }
+
+ foreach my $k (keys %$VAR1) {
+ $self->{$k} = $VAR1->{$k};
+ }
+
+ return 1;
+}
+
+=head1 FUNCTION
+
+ load_old - load old configuration format
+
+=cut
+
+sub load_old
+{
+ my ($self) = @_ ;
+
unless (open(FP, $self->{config_file}))
{
return $self->error("$self->{config_file} : $!");
}
- while (my $line = <FP>)
+ while (my $line = <FP>)
{
chomp($line);
my ($k, $v) = split(/\s*=\s*/, $line, 2);
- $self->{$k} = $v;
+ if ($k_re{$k}) {
+ $self->{$k} = $v;
+ }
}
close(FP);
{
my ($self) = @_ ;
- unless (open(FP, ">$self->{config_file}"))
- {
- return $self->error("$self->{config_file} : $!");
+ if ($self->{ach_list}) {
+ # shortcut for display_begin
+ $self->{achs} = [ map {{ name => $_ }}
+ keys %{$self->{ach_list}}
+ ];
}
-
- foreach my $k (keys %$self)
+
+ unless (open(FP, ">$self->{config_file}"))
{
- next unless (exists $k_re{$k}) ;
- print FP "$k = $self->{$k}\n";
+ return $self->error("$self->{config_file} : $!\n" .
+ "You must add this to your config file\n"
+ . Data::Dumper::Dumper($self));
}
+ print FP Data::Dumper::Dumper($self);
+
close(FP);
return 1;
}
sub view
{
my ($self) = @_ ;
-
- $self->display($self, "config_view.tpl");
+ $self->display($self, "config_view.tpl");
}
sub modify
}
}
- $self->display($self, "config_view.tpl");
+ $self->view();
if ($self->{error}) { # an error as occured
$self->display($self, 'error.tpl');
=cut
-# TODO : get autochanger definition from config/dump file
-my $ach_list ;
-
-sub get
-{
- my ($name, $bweb) = @_;
-
- unless ($name) {
- return $bweb->error("Can't get your autochanger name ach");
- }
-
- unless ($ach_list) {
- unless (get_defined_ach($bweb)) {
- return undef;
- }
- }
-
- my $a = $ach_list->{$name};
-
- unless ($a) {
- $bweb->error("Can't get your autochanger $name from your ach_list");
- return undef;
- }
-
- $a->{bweb} = $bweb;
-
- return $a;
-}
-
-sub get_defined_ach
-{
- my ($bweb) = @_;
- if (defined $bweb->{info}->{ach_list}) {
- if (open(FP, "<$bweb->{info}->{ach_list}")) {
- my $f=''; my $tmpbuffer;
- while(read FP,$tmpbuffer,4096)
- {
- $f .= $tmpbuffer;
- }
- close(FP);
- no strict; # I have no idea of the contents of the file
- eval '$ach_list = ' . $f ;
- use strict;
- } else {
- return $bweb->error("Can't open $bweb->{info}->{ach_list} $!");
- }
- } else {
- return $bweb->error("Can't find your ach_list file in your configuration");
- }
-
- $bweb->debug($ach_list);
-
- return 1;
-}
-
-sub register
-{
- my ($ach, $bweb) = @_;
- my $err;
-
- if (defined $bweb->{info}->{ach_list})
- {
- unless ($ach_list) {
- get_defined_ach($bweb) ;
- }
-
- $ach_list->{$ach->{name}} = $ach;
-
- if (open(FP, ">$bweb->{info}->{ach_list}")) {
- print FP Data::Dumper::Dumper($ach_list);
- close(FP);
- } else {
- $err = $!;
- $err .= "\nCan you put this in $bweb->{info}->{ach_list}\n";
- $err .= Data::Dumper::Dumper($ach_list);
- }
- } else {
- $err = "ach_list isn't defined";
- }
-
- if ($err) {
- return $bweb->error("Can't find to your ach_list (see bweb configuration) $err");
- }
-
- return 1;
-}
-
sub new
{
my ($class, %arg) = @_;
use DBI;
use POSIX qw/strftime/;
-our $bpath="/usr/local/bacula";
-our $bconsole="$bpath/sbin/bconsole -c $bpath/etc/bconsole.conf";
-
our $cur_id=0;
=head1 VARIABLE
=cut
our %sql_func = (
- Pg => {
- UNIX_TIMESTAMP => '',
- FROM_UNIXTIME => '',
- TO_SEC => " interval '1 second' * ",
- SEC_TO_INT => "SEC_TO_INT",
- SEC_TO_TIME => '',
- },
- mysql => {
- UNIX_TIMESTAMP => 'UNIX_TIMESTAMP',
- FROM_UNIXTIME => 'FROM_UNIXTIME',
- SEC_TO_INT => '',
- TO_SEC => '',
- SEC_TO_TIME => 'SEC_TO_TIME',
- },
- );
+ Pg => {
+ UNIX_TIMESTAMP => '',
+ FROM_UNIXTIME => '',
+ TO_SEC => " interval '1 second' * ",
+ SEC_TO_INT => "SEC_TO_INT",
+ SEC_TO_TIME => '',
+ 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) ",
+ },
+ mysql => {
+ UNIX_TIMESTAMP => 'UNIX_TIMESTAMP',
+ FROM_UNIXTIME => 'FROM_UNIXTIME',
+ SEC_TO_INT => '',
+ TO_SEC => '',
+ SEC_TO_TIME => 'SEC_TO_TIME',
+ MATCH => " REGEXP ",
+ STARTTIME_DAY => " DATE_FORMAT(StartTime, '%Y-%m-%d') ",
+ STARTTIME_HOUR => " DATE_FORMAT(StartTime, '%Y-%m-%d %H') ",
+ STARTTIME_MONTH => " DATE_FORMAT(StartTime, '%Y-%m') ",
+ STARTTIME_PHOUR=> " DATE_FORMAT(StartTime, '%H') ",
+ STARTTIME_PDAY => " DATE_FORMAT(StartTime, '%d') ",
+ STARTTIME_PMONTH => " DATE_FORMAT(StartTime, '%m') ",
+ },
+ );
sub dbh_selectall_arrayref
{
{
my ($self) = @_;
+ my $where='';
+ my $arg = $self->get_form("client", "qre_client");
+
+ if ($arg->{qre_client}) {
+ $where = "WHERE Name $self->{sql}->{MATCH} $arg->{qre_client} ";
+ } elsif ($arg->{client}) {
+ $where = "WHERE Name = '$arg->{client}' ";
+ }
+
my $query = "
SELECT Name AS name,
Uname AS uname,
AutoPrune AS autoprune,
FileRetention AS fileretention,
JobRetention AS jobretention
-
FROM Client
+$where
";
my $all = $self->dbh_selectall_hashref($query, 'name') ;
$_->{jobretention} = human_sec($_->{jobretention});
}
- my $arg = { ID => $cur_id++,
+ my $dsp = { ID => $cur_id++,
clients => [ values %$all] };
- $self->display($arg, "client_list.tpl") ;
+ $self->display($dsp, "client_list.tpl") ;
}
sub get_limit
$label = "last " . human_sec($arg{age});
}
+ if ($arg{groupby}) {
+ $limit .= " GROUP BY $arg{groupby} ";
+ }
+
if ($arg{order}) {
$limit .= " ORDER BY $arg{order} ";
}
media => 1,
ach => 1,
jobtype=> 1,
+ graph => 1,
+ gtype => 1,
+ type => 1,
);
my %opt_p = ( # option with path
+ fileset=> 1,
mtxcmd => 1,
precmd => 1,
device => 1,
);
-
+
foreach my $i (@what) {
if (exists $opt_i{$i}) {# integer param
my $value = CGI::param($i) || $opt_i{$i} ;
if ($value =~ /^([\w\d\.-]+)$/) {
$ret{$i} = $1;
}
+
} elsif ($i =~ /^j(\w+)s$/) { # quote join args
my @value = CGI::param($1) ;
if (@value) {
$ret{db_filesets} = [sort {lc($a->{fileset}) cmp lc($b->{fileset}) }
values %$filesets] ;
-
}
if ($what{db_jobnames}) {
$ret{db_jobnames} = [sort {lc($a->{jobname}) cmp lc($b->{jobname}) }
values %$jobnames] ;
-
}
if ($what{db_devices}) {
$ret{db_devices} = [sort {lc($a->{name}) cmp lc($b->{name}) }
values %$devices] ;
-
}
return \%ret;
my ($self) = @_;
my $fields = $self->get_form(qw/age level status clients filesets
- db_clients limit db_filesets width height
- qclients qfilesets qjobnames db_jobnames/);
+ graph gtype type
+ db_clients limit db_filesets width height
+ qclients qfilesets qjobnames db_jobnames/);
my $url = CGI::url(-full => 0,
-query => 1);
$url =~ s/^.+?\?//; # http://path/to/bweb.pl?arg => arg
- my $type = CGI::param('graph') || '';
- if ($type =~ /^(\w+)$/) {
- $fields->{graph} = $1;
- }
-
- my $gtype = CGI::param('gtype') || '';
- if ($gtype =~ /^(\w+)$/) {
- $fields->{gtype} = $1;
- }
-
# this organisation is to keep user choice between 2 click
# TODO : fileset and client selection doesn't work
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'
$where
$limit
";
my ($where, %elt) = $self->get_param('pool',
'location');
- my $arg = $self->get_form('jmedias');
+ my $arg = $self->get_form('jmedias', 'qre_media');
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";
+ }
my $query="
SELECT Media.VolumeName AS volumename,
$_->{bytes} = human_size($_->{bytes}) ;
}
+ $query = "
+SELECT LocationLog.Date AS date,
+ Location.Location AS location,
+ LocationLog.Comment AS comment
+ FROM Media,LocationLog INNER JOIN Location ON (LocationLog.LocationId = Location.LocationId)
+ WHERE Media.MediaId = LocationLog.MediaId
+ AND Media.VolumeName = $mq
+";
+
+ my $logtxt = '';
+ my $log = $self->dbh_selectall_arrayref($query) ;
+ if ($log) {
+ $logtxt = join("\n", map { ($_->[0] . ' ' . $_->[1] . ' ' . $_->[2])} @$log ) ;
+ }
+
$self->display({ jobs => [ values %$jobs ],
+ LocationLog => $logtxt,
%$media },
"display_media_zoom.tpl");
}
(SELECT VolStatus FROM Media WHERE VolumeName = '$media')
)
";
-
+ $self->dbh_do($query);
$self->debug($query);
}
return $self->error("Can't get media selection");
}
- my $a = Bweb::Autochanger::get($arg->{ach}, $self);
+ my $a = $self->ach_get($arg->{ach});
unless ($a) {
return 0;
}
# TODO : make this internal to not eject tape ?
use Bconsole;
+
+sub ach_get
+{
+ my ($self, $name) = @_;
+
+ unless ($name) {
+ return $self->error("Can't get your autochanger name ach");
+ }
+
+ unless ($self->{info}->{ach_list}) {
+ return $self->error("Could not find any autochanger");
+ }
+
+ my $a = $self->{info}->{ach_list}->{$name};
+
+ unless ($a) {
+ $self->error("Can't get your autochanger $name from your ach_list");
+ return undef;
+ }
+
+ $a->{bweb} = $self;
+
+ return $a;
+}
+
+sub ach_register
+{
+ my ($self, $ach) = @_;
+
+ $self->{info}->{ach_list}->{$ach->{name}} = $ach;
+
+ $self->{info}->save();
+
+ return 1;
+}
+
+sub ach_edit
+{
+ my ($self) = @_;
+ my $arg = $self->get_form('ach');
+ if (!$arg->{ach}
+ or !$self->{info}->{ach_list}
+ or !$self->{info}->{ach_list}->{$arg->{ach}})
+ {
+ return $self->error("Can't get autochanger name");
+ }
+
+ my $ach = $self->{info}->{ach_list}->{$arg->{ach}};
+
+ my $i=0;
+ $ach->{drives} =
+ [ map { { name => $_, index => $i++ } } @{$ach->{drive_name}} ] ;
+
+ my $b = $self->get_bconsole();
+
+ my @storages = $b->list_storage() ;
+
+ $ach->{devices} = [ map { { name => $_ } } @storages ];
+
+ $self->display($ach, "ach_add.tpl");
+ delete $ach->{drives};
+ delete $ach->{devices};
+ return 1;
+}
+
+sub ach_del
+{
+ my ($self) = @_;
+ my $arg = $self->get_form('ach');
+
+ if (!$arg->{ach}
+ or !$self->{info}->{ach_list}
+ or !$self->{info}->{ach_list}->{$arg->{ach}})
+ {
+ return $self->error("Can't get autochanger name");
+ }
+
+ delete $self->{info}->{ach_list}->{$arg->{ach}} ;
+
+ $self->{info}->save();
+ $self->{info}->view();
+}
+
sub ach_add
{
my ($self) = @_;
my $arg = $self->get_form('ach', 'mtxcmd', 'device', 'precmd');
- my $b = new Bconsole(pref => $self->{info});
+ my $b = $self->get_bconsole();
my @storages = $b->list_storage() ;
unless ($arg->{ach}) {
}
my @drives ;
- foreach my $drive (CGI::param('drive'))
+ foreach my $drive (CGI::param('drives'))
{
unless (grep(/^$drive$/,@storages)) {
return $self->error("Can't find $drive in storage list");
device => $arg->{device},
mtxcmd => $arg->{mtxcmd});
- return Bweb::Autochanger::register($a, $self) ;
+ $self->ach_register($a) ;
+
+ $self->{info}->view();
}
sub delete
my ($self) = @_;
my $arg = $self->get_form('jobid');
- my $b = new Bconsole(pref => $self->{info});
-
if ($arg->{jobid}) {
+ my $b = $self->get_bconsole();
my $ret = $b->send_cmd("delete jobid=\"$arg->{jobid}\"");
+
$self->display({
content => $b->send_cmd("delete jobid=\"$arg->{jobid}\""),
title => "Delete a job ",
return $self->error("Bad autochanger name");
}
- my $b = new Bconsole(pref => $self->{info});
+ my $b = $self->get_bconsole();
print "<pre>" . $b->update_slots($ach) . "</pre>";
}
unless ($row) {
return $self->error("Can't find $arg->{jobid} in catalog");
}
-
$query = "
SELECT Time AS time, LogText AS log
pool => 'Scratch',
slots => $slots) ;
print "</pre>";
+ $b->close();
}
sub purge
my @volume = CGI::param('media');
+ unless (@volume) {
+ return $self->error("Can't get media selection");
+ }
+
my $b = new Bconsole(pref => $self->{info}, timeout => 60);
$self->display({
title => "Purge media",
name => "purge volume=" . join(' volume=', @volume),
}, "command.tpl");
+ $b->close();
}
sub prune
{
my ($self) = @_;
+ my @volume = CGI::param('media');
+ unless (@volume) {
+ return $self->error("Can't get media selection");
+ }
+
my $b = new Bconsole(pref => $self->{info}, timeout => 60);
- my @volume = CGI::param('media');
$self->display({
content => $b->prune_volume(@volume),
title => "Prune media",
name => "prune volume=" . join(' volume=', @volume),
}, "command.tpl");
+
+ $b->close();
}
sub cancel_job
my $arg = $self->get_form('jobid');
unless ($arg->{jobid}) {
- return $self->error('Bad jobid');
+ return $self->error("Can't get jobid");
}
- my $b = new Bconsole(pref => $self->{info});
+ my $b = $self->get_bconsole();
$self->display({
content => $b->cancel($arg->{jobid}),
title => "Cancel job",
}, "command.tpl");
}
+sub fileset_view
+{
+ # Warning, we display current fileset
+ my ($self) = @_;
+
+ my $arg = $self->get_form('fileset');
+
+ if ($arg->{fileset}) {
+ my $b = $self->get_bconsole();
+ my $ret = $b->get_fileset($arg->{fileset});
+ $self->display({ fileset => $arg->{fileset},
+ %$ret,
+ }, "fileset_view.tpl");
+ } else {
+ $self->error("Can't get fileset name");
+ }
+}
+
sub director_show_sched
{
my ($self) = @_ ;
my $arg = $self->get_form('days');
- my $b = new Bconsole(pref => $self->{info}) ;
-
+ my $b = $self->get_bconsole();
my $ret = $b->director_get_sched( $arg->{days} );
$self->display({
return $self->error("Can't find job name");
}
- my $b = new Bconsole(pref => $self->{info}) ;
+ my $b = $self->get_bconsole();
my $cmd;
if ($what) {
}, "command.tpl");
}
+sub get_bconsole
+{
+ my ($self) = @_;
+ return new Bconsole(pref => $self->{info});
+}
+
sub run_job_select
{
my ($self) = @_;
- $b = new Bconsole(pref => $self->{info});
+ my $b = $self->get_bconsole();
- my $joblist = [ map { { name => $_ } } split(/\r\n/, $b->send_cmd(".job")) ];
+ my $joblist = [ map { { name => $_ } } $b->list_job() ];
$self->display({ Jobs => $joblist }, "run_job.tpl");
}
sub run_job_mod
{
my ($self) = @_;
- $b = new Bconsole(pref => $self->{info});
+ my $b = $self->get_bconsole();
my $job = CGI::param('job') || '';
my $info = $b->send_cmd("show job=\"$job\"");
my $attr = $self->run_parse_job($info);
- my $jobs = [ map {{ name => $_ }} split(/\r\n/, $b->send_cmd(".job")) ];
+ my $jobs = [ map {{ name => $_ }} $b->list_job() ];
- my $pools = [ map { { name => $_ } } split(/\r\n/, $b->send_cmd(".pool")) ];
- my $clients = [ map { { name => $_ } } split(/\r\n/, $b->send_cmd(".client")) ];
- my $filesets= [ map { { name => $_ } } split(/\r\n/, $b->send_cmd(".fileset")) ];
- my $storages= [ map { { name => $_ } } split(/\r\n/, $b->send_cmd(".storage")) ];
+ my $pools = [ map { { name => $_ } } $b->list_pool() ];
+ my $clients = [ map { { name => $_ } }$b->list_client()];
+ my $filesets= [ map { { name => $_ } }$b->list_fileset() ];
+ my $storages= [ map { { name => $_ } }$b->list_storage()];
$self->display({
jobs => $jobs,
sub run_job
{
my ($self) = @_;
- $b = new Bconsole(pref => $self->{info});
+ my $b = $self->get_bconsole();
- my $jobs = [ map {{ name => $_ }} split(/\r\n/, $b->send_cmd(".job")) ];
+ my $jobs = [ map {{ name => $_ }} $b->list_job() ];
$self->display({
jobs => $jobs,
sub run_job_now
{
my ($self) = @_;
- $b = new Bconsole(pref => $self->{info});
+ my $b = $self->get_bconsole();
# TODO: check input (don't use pool, level)