Last active
May 19, 2018 02:43
-
-
Save gboudreau/91b8866e3adb19965897ecd80a12525b to your computer and use it in GitHub Desktop.
duplicacy monitoring of incoming backups - email notifications when 3d / 7d without backup
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/php | |
<?php | |
/* | |
Instructions: | |
- Download this script from : https://gist.githubusercontent.com/gboudreau/91b8866e3adb19965897ecd80a12525b/raw/duplicacy-monitoring.php | |
- Make it executable: chmod +x duplicacy-monitoring.php | |
- Obtain the Duplicacy CLI from https://github.com/gilbertchen/duplicacy/releases | |
- Configure the script by modifying the variables below. | |
- Create an empty folder to use as 'duplicacy_monitoring_path' | |
- Test the script manually: /path/to/duplicacy-monitoring.php | |
This will allow you to enter the encryption password for the backups, if any. | |
- Schedule it using cron: | |
0 9 * * * /path/to/duplicacy-monitoring.php | |
*/ | |
// Path to the duplicacy executable | |
$duplicacy_exec = '/usr/local/bin/duplicacy'; | |
// Where backups are stored (there is a 'snapshots' folder in there) | |
$backup_paths = [ | |
"/mnt/hdd5/backups/duplicacy/Guillaumes-MacBook-Pro-gb-gb", | |
]; | |
// An empty folder that will be used to setup local repositories used stricly for monitoring; it needs to already exist. | |
// !!! If you use encryption, make sure this folder is not accessible to others, as it will contain your backup password(s) !!! | |
$duplicacy_monitoring_path = '/home/you/duplicacy-monitoring'; | |
// Send email notifications to this address, when there has been no backups for X days | |
$sysdmin_email = '[email protected]'; | |
// No backups since 3 days = 1st notif; no backups since 7 days = 2nd notif | |
// You can use d (days) or h (hours) suffixes; notifications will be sent when the last backup was last made between X and X+1 (days or hours), so if you use days here, schedule the cron job to run once a day, otherwise, run it every hour. | |
$notifications = [ | |
'3d' => 'Warning', | |
'7d' => 'Error' | |
]; | |
// Configuration ends here! | |
$hostname = exec('hostname -f'); | |
$duplicacy_monitoring_path = '/' . trim($duplicacy_monitoring_path, '/'); | |
foreach ($backup_paths as $k => $backup_path) { | |
$backup_paths[$k] = '/' . trim($backup_path, '/'); | |
if (!file_exists("$backup_path/snapshots/")) { | |
die("Error: missing 'snapshots' folder in backup_bath '$backup_path'. Exiting.\n"); | |
} | |
if ($duplicacy_monitoring_path == $backup_path || strpos($duplicacy_monitoring_path, "$backup_path/") !== FALSE) { | |
die("Error: 'duplicacy_monitoring_path' ($duplicacy_monitoring_path) can't be inside 'backup_path' ($backup_path). Exiting.\n"); | |
} | |
} | |
$report_date = date('Y-m-d H:i'); | |
foreach ($backup_paths as $backup_path) { | |
foreach (glob("$backup_path/snapshots/*") as $snapshot_path) { | |
$snapshot_id = basename($snapshot_path); | |
if (!file_exists("$duplicacy_monitoring_path/$snapshot_id")) { | |
mkdir("$duplicacy_monitoring_path/$snapshot_id"); | |
chdir("$duplicacy_monitoring_path/$snapshot_id"); | |
chmod("$duplicacy_monitoring_path/$snapshot_id", 0700); | |
do { | |
$encrypted = strtolower(readline("Is the backup for '$snapshot_id' encrypted? (y/n) ")); | |
} while (array_search($encrypted, ['y', 'n']) === FALSE); | |
$extra_params = ''; | |
if ($encrypted === 'y') { | |
$extra_params = '-e '; | |
} | |
passthru(escapeshellarg($duplicacy_exec) . ' init ' . $extra_params . escapeshellarg($snapshot_id) . ' ' . escapeshellarg($backup_path)); | |
chmod("$duplicacy_monitoring_path/$snapshot_id/.duplicacy", 0700); | |
chmod("$duplicacy_monitoring_path/$snapshot_id/.duplicacy/preferences", 0600); | |
if ($encrypted === 'y') { | |
$password = readline("Enter storage password (again): "); | |
exec(escapeshellarg($duplicacy_exec) . " set -key password -value " . escapeshellarg($password)); | |
_log("!!! WARNING !!! Password stored in $duplicacy_monitoring_path/$snapshot_id/.duplicacy/preferences ; make sure nobody can read this file !!!"); | |
} | |
} | |
chdir("$duplicacy_monitoring_path/$snapshot_id"); | |
$last_backup = exec(escapeshellarg($duplicacy_exec) . ' list | grep -v "Storage set to " | tail -1'); | |
if (empty($last_backup) || $last_backup == "Repository has not been initialized") { | |
_log("No backup (snapshot) found for id '$snapshot_id'; i.e. the first backup didn't complete yet."); | |
} else { | |
if (!preg_match("/Snapshot (.+) revision ([\d]+) created at (\d+-\d+-\d+ \d+:\d+)/", $last_backup, $re)) { | |
_log("ERROR! Couldn't parse output from 'duplicacy list': $last_backup\n"); | |
continue; | |
} | |
$found_snapshot_id = $re[1]; | |
$revision_number = $re[2]; | |
$last_backup_date = $re[3]; | |
$seconds_since_last_backup = time() - strtotime($last_backup_date); | |
$since_last_backup_h = $seconds_since_last_backup / 3600; | |
$since_last_backup_d = $since_last_backup_h / 24; | |
_log("Last backup from $snapshot_id was " . round($since_last_backup_h) . " hours (" . round($since_last_backup_d, 1) . " days) ago..."); | |
foreach ($notifications as $notif => $level) { | |
if (!preg_match('/^(\d+)([dh])$/', $notif, $re)) { | |
_log("WARNING; wrong 'notification' definition: $notif. Expected format: '#d' or '#h'. Skipping."); | |
continue; | |
} | |
$trigger_number = $re[1]; | |
$trigger_unit = $re[2]; | |
if (${"since_last_backup_$trigger_unit"} < $trigger_number) { | |
_log(" OK."); | |
break; | |
} else { | |
_log(" Oh noes! Backup from '$snapshot_id' is missing for more than $notif!!"); | |
if (${"since_last_backup_$trigger_unit"} < $trigger_number+1) { | |
_log(" Sending email notification to $sysdmin_email"); | |
mail($sysdmin_email, "[$level] Backup missing for $snapshot_id !", "No duplicacy backup was made in the last $notif from $snapshot_id to $hostname.\nLast backup found: $last_backup\n"); | |
break; | |
} else { | |
_log(" Not sending another email notification; was already sent not so long ago."); | |
} | |
} | |
} | |
} | |
} | |
} | |
function _log($log) { | |
global $report_date; | |
error_log("[$report_date] $log"); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment