On 9/16/25 12:39, Gary Dale wrote:
Internet searches have been futile. The ones that actually mention
bconsole suggest what I've been trying to do should work, but it doesn't.
Through my initial setup struggles, my storage volumes have become a
disjointed mess. To make matters worse, there are close to a hundred
volumes showing "Error" when I "List Volumes". I have only about 20 that
show up as physical disk volumes. And automatic volume creation seems to
be failing. All my backups for the last 5 days have failed.
...
Being able to put bconsole commands into a bash script seems like
something that will be generally useful but I can't find a way to make
it work.
Any ideas?
Willing to try a Perl script instead?
Try this one. It does exactly what you're trying to do, including
detecting errored volumes.
You'll need DBD::mysql or DBD::mariadb if you con't have them already
installed and you're using a mysql/mariadb back end. If you use
PostgreSQL you'll have to make some changes. Try it with -t first as a
dry-run to make sure it's going to do what you want it to.
--
Phil Stracchino
Fenian House Publishing
[email protected]
[email protected]
Landline: +1.603.293.8485
Mobile: +1.603.998.6958
#!/usr/bin/perl
use strict;
use DBD::mysql;
use Getopt::Long;
use IPC::Open2;
use IO::Handle;
my $bconsole = '/usr/sbin/bconsole';
my (%opts, @purged, @orphans, $pid, $volume_dir);
Getopt::Long::Configure('bundling');
GetOptions(\%opts,
'verbose|v',
'test|t');
$volume_dir = shift() || '/bpool/bacula';
print "Volume directory is $volume_dir\n" if ($opts{verbose});;
my ($IN, $OUT) = (IO::Handle->new(), IO::Handle->new());
$pid = open2($OUT, $IN, $bconsole) || die "Unable to open bconsole";
if (check_volumes())
{
my $deleted;
if (scalar @purged)
{
printf("Bacula reports the following purged or errored
volumes:\n\t%s\n",
join("\n\t", @purged)) if ($opts{verbose});
$deleted = delete_volumes(@purged) unless ($opts{test});
print "$deleted purged volumes deleted.\n" if ($opts{verbose});
}
elsif ($opts{verbose})
{
print "No purged volumes found to delete.\n";
}
if (scalar @orphans)
{
printf("The following orphaned volumes were found:\n\t%s\n",
join("\n\t", @orphans)) if ($opts{verbose});
$deleted = delete_orphans(@orphans) unless ($opts{test});
print "$deleted orphan volume files deleted.\n" if ($opts{verbose});
}
elsif ($opts{verbose})
{
print "No orphaned volumes found to delete.\n";
}
}
elsif ($opts{verbose})
{
print "Nothing found to do.\n";
}
print $IN "exit\n";
waitpid($pid, 0);
exit (0);
sub check_volumes
{
my $dividers = 0;
my (@row, @files);
print $IN "list volumes pool=Scratch\n";
for (;;)
{
my $resp = <$OUT>;
last if ($resp =~ /No results to list./);
$dividers++ if ($resp =~ /^[\+\-]+$/);
last if ($dividers == 3);
@row = split(/\s+/, $resp);
push (@purged, $row[3]) if ($row[5] eq 'Purged');
}
print $IN "list volumes\n";
for (;;)
{
my $resp = <$OUT>;
last if ($resp =~ /No results to list.|Pool: Scratch/ );
$dividers++ if ($resp =~ /^[\+\-]+$/);
@row = split(/\s+/, $resp);
push (@purged, $row[3]) if ($row[5] eq 'Error');
}
if ( -d $volume_dir )
{
my ($dbh, $sth, %volumes);
print "Scanning for orphans in $volume_dir ...\n" if ($opts{verbose});
opendir (DIR, $volume_dir);
@files = sort(grep(!/^\.{1,2}$/, readdir (DIR)));
closedir (DIR);
printf ("Files found on disk:\n\t%s\n",
join("\n\t", @files)) if ($opts{verbose});
$dbh = open_db();
$sth = $dbh->prepare('SELECT volumeName FROM bacula.Media') || die
"Error:" . $dbh->errstr . "\n";
$sth->execute || die "Error:" . $dbh->errstr . "\n";
while (my $ref = $sth->fetchrow_arrayref)
{
$volumes{$$ref[0]} = 1;
}
$sth->finish();
$dbh->disconnect();
printf ("Volumes found in media table:\n\t%s\n",
join("\n\t", sort keys %volumes)) if ($opts{verbose});
foreach my $f (@files)
{
next unless ($f =~ /^(FULL|DIFF|INCR)/);
print "Checking file $f against volume list\n" if ($opts{verbose});
push (@orphans, $f) if (not defined $volumes{$f});
}
}
return (scalar @purged + scalar @orphans);
}
sub delete_volumes
{
my $count = 0;
foreach my $vol (@_)
{
my $l;
my $file = $volume_dir.'/'.$vol;
print "Deleting volume $vol from catalog ... " if ($opts{verbose});
print $IN "delete volume=$vol yes\n";
$l = <$OUT>;
$l = <$OUT>;
print "Done.\nDeleting volume $file from disk ... " if ($opts{verbose});
if (-f $file)
{
$count++;
unlink ($file);
}
print "Done.\n" if ($opts{verbose});
}
return ($count);
}
sub delete_orphans
{
my $count = 0;
foreach my $vol (@_)
{
my $l;
my $file = $volume_dir.'/'.$vol;
print "Deleting volume $file from disk ... " if ($opts{verbose});
if (-f $file)
{
$count++;
unlink ($file);
}
print "Done.\n" if ($opts{verbose});
}
return ($count);
}
sub open_db
{
my ($dsn, $dbh);
$dsn = $opts{socket} && -e $opts{socket}
?
sprintf("DBI:mysql:mysql:host=localhost;mysql_read_default_file=%s;mysql_read_default_group=mysql;mysql_socket=%s;",
'/root/.my.cnf',
$opts{socket})
:
sprintf("DBI:mysql:mysql:host=localhost;mysql_read_default_file=%s;mysql_read_default_group=mysql;",
'/root/.my.cnf');
$dbh = DBI->connect($dsn, undef, undef, {RaiseError => 1, AutoCommit => 0});
$dbh->do(sprintf('SET SESSION wait_timeout = %d',
$opts{interval} > 3500 ? $opts{interval} + 100 : 3600)) ||
die "Could not set session wait_timeout";
return ($dbh);
}
_______________________________________________
Bacula-users mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/bacula-users