Skip to content

Instantly share code, notes, and snippets.

@DirkR
Created March 25, 2011 07:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save DirkR/886516 to your computer and use it in GitHub Desktop.
Save DirkR/886516 to your computer and use it in GitHub Desktop.
dyplyreport.pl - a duply syslog wrapper. This script runs duply (a duplicity backup wrapper), filters all the bias and writes all important information into syslog.
#!/usr/bin/env perl
# vi: sw=2 ts=2 et
# (C) 2011 Dirk Ruediger <dirk@niebegeg.net>
# Feel free do hack or adopt it!
#
# A syslog wrapper for duply. It runs duply, filers "bias" out and
# writes all important and unexpected messages to syslog.
#
# usage:
# Like duply. all arguments are passed to duply
#
# duplyreport.pl profilename backup_verify_purge
#
# etc.
# The script should not produce any output to console.
use warnings;
use strict;
use Getopt::Long;
use Date::Format;
use File::Basename;
use Proc::PID::File;
my $procname = 'duply';
my $program = basename($0);
my $state = 'nostate';
my $buffer = '';
my @profiles = ();
my $mailto = '';
my $syslog = 0;
my $help = 0;
my $mail_content = '';
my $result = GetOptions ("syslog|s" => \$syslog,
"help|h" => \$help,
"profile|p=s" => \@profiles,
"mail|m=s" => \$mailto);
if ($help) {
print <<"USAGE";
Usage: $program [-s] [-h] [-m address] -p profile [-p profile [-p profile...]] action
-s Log to syslog
-m address Mail log to address
-h Print this usage message
-p profile define a duply profile, multiple profiles are possible.
If no profiles are defined, then take "all profiles that do not contain a file named 'skip'".
action duplicity action to be performed, defaults to "backup"
USAGE
exit 0;
}
if (scalar(@ARGV) == 0) {
push(@ARGV, 'backup');
}
if (scalar(@profiles) == 0) {
@profiles = get_all_profiles();
}
my $timestr = time2str('%c', time);
if ($syslog) {
use Unix::Syslog qw(:subs :macros);
openlog "duply", LOG_PID | LOG_INFO, LOG_LOCAL0;
}
if (Proc::PID::File->running()) {
logger("error", "$program is already running. I'll exit here.");
exit(0);
}
if($mailto ne '') {
if ($mailto !~ m/^[A-Za-z0-9](([_\.\-]?[a-zA-Z0-9]+)*)@([A-Za-z0-9]+)(([\.\-]?[a-zA-Z0-9]+)*)\.([A-Za-z]{2,})$/) {
my $message = "Invalid mail address format: $mailto";
$mailto = '';
logger('error', $message);
exit 1;
}
}
sub get_all_profiles {
my @profiles = ();
foreach my $config_file (glob('/etc/duply/*/conf')) {
my $profile_folder = dirname($config_file);
if (! -f "$profile_folder/skip") {
push @profiles, basename($profile_folder);
}
}
return @profiles;
}
sub logger {
my $level = shift || 'info';
my $message = shift || '';
if ($syslog) {
my $loglevel = LOG_INFO;
if ($level ne 'error') {
$loglevel = LOG_ERR;
}
syslog($loglevel, $message);
}
if ($mailto ne '') {
$mail_content .= ($level ne 'info' ? uc($level) : '') . " $message\n";
}
if (! $syslog && $mailto eq '') {
print (($level ne 'info' ? uc($level) : '') . " $message\n");
}
}
foreach my $profile (@profiles) {
my $proc;
unless (open($proc, "LC_ALL=C $procname $profile @ARGV 2>&1 |")) {
logger('error', "Could not start $procname process: $!");
die "Could not start $procname process: $!";
}
while (<$proc>) {
chomp;
next if
/^(Using|Autoset|Test -|Cleanup -|Deleting local) / ||
/^Skipping n\/a script / ||
/^Reading globbing filelist / ||
/^Synchronizing remote metadata to local cache/ ||
/^Local and Remote metadata are synchronized, no sync needed/ ||
/^Generating delta - (new|deleted|changed) file/ ||
/^-+\[ Backup Statistics \]-+/ ||
/^gpg: Signature made / ||
/^NcFTP version is [0-9.]+/ ||
/^(StartTime|EndTime|ElapsedTime|SourceFiles|NewFileSize|ChangedFileSize|ChangedDeltaSize|DeltaEntries|RawDeltaSize|TotalDestinationSizeChange|Errors) [0-9.]+/ ||
/^WARNING:$/ ||
/^(Duplicity version '[0-9.]+' does not support providing the password as|env var for rsync backend. For security reasons you should consider to|update to a version greater than '[0-9.]+' of duplicity.)/ ||
/^Verify complete: \d+ files compared, 0 differences found/ ||
/^Copying duplicity\S* to local cache/ ||
/^Skipping n\/a script '\S+\/(pre|post)'\./ ||
/^Running '\/\S+\/(pre|post)' - OK/ ||
/^Reuse configured PASSPHRASE as SIGN_PASSPHRASE/ ||
/Import of duplicity.backends.\w+backend Failed: No module named/;
s/^--- //;
s/ at [0-9:.]+ - Runtime [0-9:.-]+ ---//;
next if /^\s*$/;
if (m/^Start duply/) {
logger('info', "**Start 'duply $profile @ARGV'**");
next;
}
if (m/^(Changed|New|Deleted)Files (\d+)/) {
$buffer .= ", $1: $2";
next;
}
if (m/^SourceFileSize.*\(([0-9.]+) ([MGK]B)\)/) {
$buffer = "Size=$1$2$buffer";
next;
}
if (m/^Start running command ([A-Z]+).*/) {
$state = $1;
next;
}
if (m/^No old backup sets found, nothing deleted/ && ($state eq 'PURGE')) {
next;
}
if (m/^Last full backup date/ && ($state eq 'PURGE' || $state eq 'VERIFY')) {
next;
}
if (m/Verify complete: \d+ files compared, 0 differences found/ && ($state eq 'VERIFY')) {
next;
}
if (m/^Finished state OK/) {
next;
}
if (m/^---+$/) {
logger('info', "[$state] $buffer");
$buffer = '';
next;
}
logger('info', "[$state] $_");
}
if ($mailto ne '') {
$mail_content .= "\n";
}
}
if ($syslog) {
closelog;
}
if ($mailto ne '') {
open (my $mail, "| mail -s 'Duply report: $timestr' $mailto");
print $mail $mail_content;
close $mail;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment