Re: How to run bconsole commands in a bash for loop?
Phil Stracchino <[email protected]>
| Newsgroups | gmane.comp.bacula.user |
|---|---|
| Organization | Fenian House Publishing |
| Message-ID | <[email protected]> |
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 _______________________________________________ Bacula-users mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/bacula-users
clean_volumes
(text/plain, 4.9 KB)
#!/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);
}