Subversion Repositories zfs_utils

Rev

Rev 53 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 53 Rev 54
Line 25... Line 25...
25
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
 
28
 
29
# cleanSnaps - detect and remove old ZFS snapshots named like YYYY-MM-DD-<N><d|w|m>
29
# cleanSnaps - detect and remove old ZFS snapshots named like YYYY-MM-DD-<N><d|w|m>
30
# Usage: cleanSnaps [-n] [-f] [-v] [-t <shift>] [-h]
30
# Usage: cleanSnaps [-f] [-v] [-t <shift>] [-h]
31
#   -n  dry-run (default)
-
 
32
#   -f  actually destroy snapshots
31
#   -f  actually destroy snapshots (use with care)
33
#   -v  verbose
32
#   -v  verbose
34
#   -t  time shift, simulate running at a different time. Examples:
33
#   -t  time shift, simulate running at a different time. Examples:
35
#       -t 2m   (run as if 2 months ago)
34
#       -t 2m   (run as if 2 months ago)
36
#       -t -4w  (run as if 4 weeks earlier)
35
#       -t -4w  (run as if 4 weeks earlier)
37
#       -t +3d  (run as if 3 days in the future)
36
#       -t +3d  (run as if 3 days in the future)
38
#   -h  show this help and exit
37
#   -h  show this help and exit
39
 
38
 
40
use strict;
39
use strict;
41
use warnings;
40
use warnings;
42
use Getopt::Std;
41
use Getopt::Long qw(GetOptions);
43
use Time::Piece;
42
use Time::Piece;
44
 
43
 
45
my %opts;
44
# Allow short option bundling (e.g. -fv) and accept long names
46
getopts('nfvht:', \%opts);
45
Getopt::Long::Configure('bundling');
47
 
46
 
-
 
47
my %opts;
-
 
48
GetOptions(\%opts,
-
 
49
    'force|f',        # force (actually perform destroys)
-
 
50
    'verbose|v',      # verbose
-
 
51
    'help|h',         # help
-
 
52
    'timeshift|t=s',  # time shift string (alias -t)
-
 
53
) or die "Error parsing command line options\n";
-
 
54
 
48
my $DRY_RUN = $opts{f} ? 0 : 1;    # default dry-run, -f disables dry-run
55
# Normalize options: default behaviour is dry-run unless --force is specified.
49
my $FORCE = $opts{f} ? 1 : 0;
56
my $FORCE   = (defined $opts{'force'} || defined $opts{'f'}) ? 1 : 0;
50
my $VERBOSE = $opts{v} ? 1 : 0;
57
my $VERBOSE = (defined $opts{'verbose'} || defined $opts{'v'}) ? 1 : 0;
-
 
58
my $timeshift = defined $opts{'timeshift'} ? $opts{'timeshift'} : $opts{'t'};
51
 
59
 
52
my $ZFS_CMD = $ENV{ZFS_CMD} // 'zfs';
60
my $ZFS_CMD = $ENV{ZFS_CMD} // 'zfs';
53
 
61
 
54
sub logmsg { print @_, "\n" if $VERBOSE }
62
sub logmsg { print @_, "\n" if $VERBOSE }
55
 
63
 
56
# show help and exit
64
# show help and exit
57
if ($opts{h}) {
65
if ($opts{'help'} || $opts{'h'}) {
58
    print "Usage: cleanSnaps [-n] [-f] [-v] [-t <shift>] [-h]\n";
66
    print "Usage: cleanSnaps [--force|-f] [--verbose|-v] [--timeshift|-t <shift>] [--help|-h] [pool]\n";
59
    print "  -n  dry-run (default)\n";
-
 
60
    print "  -f  actually destroy snapshots\n";
67
    print "  --force, -f         actually destroy snapshots (default: dry-run)\n";
61
    print "  -v  verbose\n";
68
    print "  --verbose, -v       verbose logging\n";
62
    print "  -t  time shift, simulate running at a different time (examples: 2m, -4w, +3d)\n";
69
    print "  --timeshift, -t     time shift, simulate running at a different time (examples: 2m, -4w, +3d)\n";
63
    print "  -h  show this help and exit\n";
70
    print "  --help, -h          show this help and exit\n";
64
    exit 0;
71
    exit 0;
65
}
72
}
66
 
73
 
67
# compute simulated "now" if the user requested a time shift
74
# compute simulated "now" if the user requested a time shift
68
# allows us to keep or remove snapshots as if we were running at a different time
75
# allows us to keep or remove snapshots as if we were running at a different time
69
# giving retention decisions less stringent on, say, backup systems
76
# giving retention decisions less stringent on, say, backup systems
70
my $now = time();
77
my $now = time();
71
if (defined $opts{t} && $opts{t} =~ /^(?:([+-]?)(\d+)([smhdwy]))$/i) {
78
if (defined $timeshift && $timeshift =~ /^(?:([+-]?)(\d+)([smhdwy]))$/i) {
72
    my ($sign, $num, $unit) = ($1, $2, lc $3);
79
    my ($sign, $num, $unit) = ($1, $2, lc $3);
73
    # default behavior: no leading sign => treat as negative (simulate running in the past)
80
    # default behavior: no leading sign => treat as negative (simulate running in the past)
74
    $sign = '-' unless defined $sign && $sign ne '';
81
    $sign = '-' unless defined $sign && $sign ne '';
75
    my %unit_secs = (
82
    my %unit_secs = (
76
        s => 1,
83
        s => 1,
Line 84... Line 91...
84
        die "Invalid time unit '$unit' in -t option; use s,m,h,d,w,y\n";
91
        die "Invalid time unit '$unit' in -t option; use s,m,h,d,w,y\n";
85
    }
92
    }
86
    my $shift_seconds = $num * $unit_secs{$unit};
93
    my $shift_seconds = $num * $unit_secs{$unit};
87
    $shift_seconds = -$shift_seconds if $sign eq '-';
94
    $shift_seconds = -$shift_seconds if $sign eq '-';
88
    $now += $shift_seconds;
95
    $now += $shift_seconds;
-
 
96
    my $ts_display = defined $timeshift ? $timeshift : $opts{t};
89
    logmsg("Simulating current time with shift $opts{t}; adjusted now -> " . scalar(localtime($now)) );
97
    logmsg("Simulating current time with shift $ts_display; adjusted now -> " . scalar(localtime($now)) );
90
}
98
}
91
 
99
 
92
my @candidates; # this will hold the candidates for removal
100
my @candidates; # this will hold the candidates for removal
93
 
101
 
94
# Optional command-line parameter: pool name to process (only snapshots from this pool will be considered)
102
# Optional command-line parameter: pool name to process (only snapshots from this pool will be considered)
Line 179... Line 187...
179
}
187
}
180
 
188
 
181
printf "Snapshots to remove (%d):\n", scalar @candidates;
189
printf "Snapshots to remove (%d):\n", scalar @candidates;
182
for my $s (@candidates) { printf "  %s\n", $s }
190
for my $s (@candidates) { printf "  %s\n", $s }
183
 
191
 
184
if ($DRY_RUN) {
192
if (!$FORCE) {
185
    print "\nDry-run: no snapshots were destroyed. Use -f to actually remove them.\n";
193
    print "\nDry-run: no snapshots were destroyed. Use -f to actually remove them.\n";
186
    exit 0;
194
    exit 0;
187
}
195
}
188
 
196
 
189
# actual removal. If any fail, report error but continue to remove rest
197
# actual removal. If any fail, report error but continue to remove rest