Last active
July 30, 2016 23:03
-
-
Save nakal/e3b2f36b6e6a32ae7b67d8aa52445516 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env perl | |
use strict; | |
use warnings; | |
my $verbose = 0; | |
my ($senddiff) = @ARGV; | |
if (defined($senddiff)) { | |
$senddiff = 1; | |
} | |
# keep snapshots per ZFS dataset | |
my $snap_poolname = "pool"; | |
my $snap_fs_home_keep = 30; | |
my %snapfs = ( | |
"$snap_poolname/ROOT/default" => $snap_fs_home_keep, | |
"$snap_poolname/ROOT/safe" => $snap_fs_home_keep, | |
"$snap_poolname/var/tmp" => $snap_fs_home_keep, | |
"$snap_poolname/var/log" => $snap_fs_home_keep, | |
"$snap_poolname/var/mail" => $snap_fs_home_keep | |
); | |
# skip reports for these ZFS datasets | |
my %nodiffs = ( | |
"$snap_poolname/usr/home" => 1, | |
"$snap_poolname/var/tmp" => 1, | |
"$snap_poolname/var/log" => 1, | |
"$snap_poolname/var/mail" => 1 | |
); | |
# add user directories | |
if (open (my $FH, "-|", "/sbin/zfs list -H -o name -r $snap_poolname/usr/home")) { | |
_log(sprintf("[$snap_poolname/usr/home] Determining user ZFS datasets\n")); | |
while (<$FH>) { | |
chomp; | |
my $fs = $_; | |
$snapfs{$fs} = $snap_fs_home_keep if (!defined($snapfs{$fs})); | |
} | |
close($FH); | |
} | |
my $snap_regex = "[0-9]{4}-[0-9]{2}-[0-9]{2}_[0-9]{2}:[0-9]{2}:[0-9]{2}"; | |
foreach (keys %snapfs) { | |
my $fs = $_; | |
if (!&ismounted($fs)) { | |
_log(sprintf("[%s] Is not mounted, skip.\n", $fs)); | |
next; | |
} | |
my $keep = $snapfs{$fs}; | |
if ($keep == 0) { | |
_log(sprintf("[%s] Is excluded, skip.\n", $fs)); | |
next; | |
} | |
_log(sprintf("[%s] Snapshotting ...\n", $fs)); | |
my $snapname = `/bin/date +%Y-%m-%d_%H:%M:%S`; | |
chomp($snapname); | |
my $snapyest = ""; | |
if ($snapname =~ m/$snap_regex/) { | |
_log(sprintf("\t -> %s@%s\n", $fs, $snapname)); | |
system("/sbin/zfs snapshot $fs" . "@" . "$snapname"); | |
my @snapshots = &list_snapshots($fs); | |
my $snapnum = scalar @snapshots; | |
_log(sprintf("[%s] Have %u snapshot(s) ...\n", $fs, $snapnum)); | |
@snapshots = reverse @snapshots; | |
my $snapsec_now = &ts_parse($snapname); | |
foreach my $snapstr (@snapshots) { | |
my $snap_sec = &ts_parse($snapstr); | |
if ($snapsec_now - $snap_sec > 23 * 60 * 60) { | |
$snapyest = $snapstr; | |
last; | |
} | |
} | |
if (!defined($nodiffs{$fs}) and $senddiff and length($snapyest) > 0) { | |
my $content = ""; | |
_log(sprintf("[%s] Will diff %s and %s...\n", $fs, $snapname, $snapyest)); | |
if (open (my $FH, "-|", "/sbin/zfs diff $fs\@". $snapyest . " $fs\@" . $snapname)) { | |
_log(sprintf("[%s] Calculating diff between %s and %s...\n", $fs, $snapname, $snapyest)); | |
while (<$FH>) { | |
my $line = $_; | |
my $addline = 1; | |
$addline = 0 if (m/\<xattrdir\>/); | |
$addline = 0 if (m/^M.*\/Thumbs.db$/); | |
$content .= $line if ($addline == 1); | |
} | |
close($FH); | |
} else { | |
_log(sprintf("[%s] Diff failed for %s and %s...\n", $fs, $snapname, $snapyest)); | |
} | |
if (length($content) > 0) { | |
# and open (my $MH, "|-", "/usr/bin/mail -s \'Latest modifications on $fs\' root")) { | |
#print $MH $content; | |
#close($MH); | |
printf("=== [%s] lastest changes =======================\n", $fs); | |
print $content . "\n"; | |
} | |
} | |
if ($snapnum > $keep) { | |
_log(sprintf("[%s] Tidying up old snapshots (keeping %u) ...\n", $fs, $keep)); | |
my @snaps_to_delete = (@snapshots)[$keep..$#snapshots]; | |
foreach (@snaps_to_delete) { | |
my $dsnap = $_; | |
_log(sprintf("[%s] Deleting snapshot %s ...\n", $fs, $dsnap)); | |
system("/sbin/zfs destroy $fs" . "@" . "$dsnap"); | |
} | |
} | |
} else { | |
die("Auto-created snapshot name $snapname does not match regexp!"); | |
} | |
} | |
exit 0; | |
sub ismounted { | |
my ($fs) = @_; | |
my $str = `/sbin/zfs get -Ho value mounted $fs`; | |
chomp $str; | |
return $str eq 'yes' ? 1 : 0; | |
} | |
sub list_snapshots { | |
my ($fs) = @_; | |
#printf("Listing snapshots under %s...\n", $fs); | |
my @out = `/sbin/zfs list -H -t snapshot -r $fs`; | |
my @ret = (); | |
foreach (@out) { | |
my $ln = $_; | |
if ($ln =~ m/^$fs@($snap_regex)\t/) { | |
my $snapname = $1; | |
#printf("Snapname: %s\n", $snapname); | |
push @ret, $snapname; | |
} | |
} | |
return sort @ret; | |
} | |
sub _log { | |
my ($msg) = @_; | |
print $msg if ($verbose); | |
} | |
sub ts_parse { | |
my ($ts) = @_; | |
#print "Parsing '$ts'\n"; | |
my @secs = `/bin/date -j -f '%Y-%m-%d_%H:%M:%S' $ts +%s`; | |
return $secs[0]; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment