3 # Copyright (C) 2000-2015 Kern Sibbald
4 # License: BSD 2-Clause; see file LICENSE-FOSS
10 manual_prune.pl -- prune volumes
14 manual_prune.pl [--bconsole=/path/to/bconsole] [--help] [--doprune] [--expired] [--fixerror] [--fileprune]
16 This program when run will manually prune all Volumes that it finds
17 in your Bacula catalog. It will respect all the Retention periods.
19 manual_prune must have access to bconsole. It will execute bconsole
20 from /opt/bacula/bin. If bconsole is in a different location,
21 you must specify the path to it with the --bconsole=... option.
23 If you do not add --doprune, you will see what the script proposes
24 to do, but it will not prune.
26 If you add --fixerror, it will change the status of any Volume
27 that is marked Error to Used so that it will be pruned.
29 If you add --expired, it will attempt to prune only those
30 Volumes where the Volume Retention period has expired.
32 If you use --fileprune, the script will prune files and pathvisibility
33 useful to avoid blocking Bacula during pruning.
35 Adding --debug will print additional debug information.
40 Copyright (C) 2008-2014 Bacula Systems SA
42 Bacula(R) is a registered trademark of Kern Sibbald.
43 The licensor of Bacula Enterprise is Bacula Systems SA,
44 Rue Galilee 5, 1400 Yverdon-les-Bains, Switzerland.
46 This file has been made available for your personal use in the
47 hopes that it will allow community users to make better use of
56 use Getopt::Long qw/:config no_ignore_case/;
64 # set to your bconsole prog
65 my $bconsole = "/opt/bacula/bin/bconsole";
69 GetOptions('help' => \$help,
70 'bconsole=s' => \$bconsole,
71 'expired' => \$expired,
73 'fixerror' => \$do_fix,
74 'fileprune' => \$do_file_prune,
75 'doprune' => \$do_prune)
76 || Pod::Usage::pod2usage(-exitval => 2, -verbose => 2) ;
79 Pod::Usage::pod2usage(-exitval => 2, -verbose => 2) ;
83 die "Can't exec $bconsole, please specify --bconsole option $!";
89 # This fix can work with File based device. Don't use it for Tape media
91 my ($fh, $file) = File::Temp::tempfile();
93 SELECT VolumeName AS \"?vol?\" FROM Media WHERE VolStatus = 'Error';
97 open(FP, "cat $file | $bconsole|") or die "Can't open $bconsole (ERR=$!), adjust your \$PATH";
100 if ($l =~ /^\s*\|\s*([\w\d:\. \-]+?)\s*\|/) {
110 if (scalar(@vol) > 0) {
111 print "Will try to fix volume in Error: ", join(",", @vol), "\n";
112 open(FP, "|$bconsole") or die "Can't send commands to $bconsole";
113 print FP map { "update volume=$_ volstatus=Used\n" } @vol;
119 if ($do_file_prune) {
120 my ($fh, $file) = File::Temp::tempfile();
124 CREATE TEMPORARY TABLE temp AS
125 SELECT DISTINCT JobId FROM Job JOIN JobMedia USING (JobId) JOIN
126 (SELECT Media.MediaId AS MediaId
128 WHERE VolStatus IN ('Full', 'Used')
129 AND ( (Media.LastWritten)
130 + interval '1 second' * (Media.VolRetention)
131 ) < NOW()) AS M USING (MediaId)
132 WHERE Job.JobFiles > 50000 AND Job.PurgedFiles=0;
133 SELECT JobId FROM temp;
134 DELETE FROM File WHERE JobId IN (SELECT JobId FROM temp);
135 DELETE FROM PathVisibility WHERE JobId IN (SELECT JobId FROM temp);
136 UPDATE Job SET PurgedFiles=1 WHERE JobId IN (SELECT JobId FROM temp);
144 SELECT DISTINCT JobId FROM Job JOIN JobMedia USING (JobId) JOIN
145 (SELECT Media.MediaId AS MediaId
147 WHERE VolStatus IN ('Full', 'Used')
148 AND ( (Media.LastWritten)
149 + interval '1 second' * (Media.VolRetention)
150 ) < NOW()) AS M USING (MediaId)
151 WHERE Job.JobFiles > 50000 AND Job.PurgedFiles=0;
157 open(FP, "cat $file | $bconsole|") or die "Can't open $bconsole (ERR=$!), adjust your \$PATH";
160 if ($debug || !$do_prune) {
169 # TODO: Fix it for SQLite
170 # works only for postgresql and MySQL at the moment
171 # One of the two query will fail, but it's not a problem
173 my ($fh, $file) = File::Temp::tempfile();
175 SELECT Media.VolumeName AS volumename,
176 Media.LastWritten AS lastwritten,
179 + interval '1 second' * (Media.VolRetention)
182 WHERE VolStatus IN ('Full', 'Used')
183 AND ( (Media.LastWritten)
184 + interval '1 second' * (Media.VolRetention)
186 SELECT Media.VolumeName AS volumename,
187 Media.LastWritten AS lastwritten,
189 Media.LastWritten + Media.VolRetention
192 WHERE VolStatus IN ('Full', 'Used')
193 AND ( Media.LastWritten + Media.VolRetention
199 open(FP, "cat $file | $bconsole|") or die "Can't open $bconsole (ERR=$!), adjust your \$PATH";
202 # | TestVolume001 | 2011-06-17 14:36:59 | 2011-06-17 14:37:00
203 if ($l =~ /^\s*\|\s*([\w\d:\. \-]+?)\s*\|\s*\d/) {
215 open(FP, "echo list volumes | $bconsole|") or die "Can't open $bconsole (ERR=$!), adjust your \$PATH";
218 # | 1 | TestVolume001 | Used
219 if ($l =~ /^\s*\|\s*\d+\s*\|\s*([\w\d-]+)\s*\|\s*Used/) {
222 if ($l =~ /^\s*\|\s*\d+\s*\|\s*([\w\d-]+)\s*\|\s*Full/) {
225 if ($l =~ /^\s*\|\s*\d+\s*\|\s*([\w\d-]+)\s*\|\s*Purged/) {
226 push @vol_purged, $1;
232 system("echo list volumes | $bconsole");
233 die "bconsole returns a non zero status, please check that you can execute it";
239 print "No Volume(s) found to prune.\n";
243 print "Attempting to to prune ", join(",", @vol), "\n";
244 open(FP, "|$bconsole") or die "Can't send commands to $bconsole";
245 print FP map { "prune volume=$_ yes\n" } @vol;
248 print "Would have attempted to prune ", join(",", @vol), "\n";
249 print "You can actually prune by specifying the --doprune option.\n"