Created
July 27, 2016 17:51
-
-
Save gwinans/96bb813c847a2ffce69ba0dafd5c39bf 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/php | |
<?php | |
$missing = <<<MISSING | |
Config file not found (/etc/inno_backup.conf) | |
Create a new file with the following keys/values: | |
user = backups | |
pass = password1234 | |
backup_dir = /backups | |
data_dir = /var/lib/mysql | |
MISSING; | |
if( ! file_exists('/etc/inno_backup.conf') ) { | |
exit($missing."\n"); | |
} | |
$conf = parse_ini_file('/etc/inno_backup.conf'); | |
$user = $conf['user']; | |
$pass = $conf['pass']; | |
$data_dir = $conf['data_dir']; | |
$backup_dir = $conf['backup_dir']; | |
$usage = <<<USAGE | |
USAGE: inno_backup [option] | |
Options: | |
full : Start a full backup. This will delete ALL incrementals and the current full snapshot. | |
incremental : Start an incremental. If no full backup exists, the script will fall back to a full backup. | |
prepare : Glues all incrementals to the full snapshot in preparation for a restore. | |
restore : Shuts down MySQL, removes content of datadir, replaces it with the backup. Dangerous. | |
reset : THIS WILL NUKE THE BACKUP DIRECTORY AND ALL CONTENT WITHIN! Only use to start clean. | |
USAGE; | |
$arg = ( isset($argv[1]) ) ? $argv[1] : ""; | |
$base_args = "--user={$user} --password={$pass}"; | |
switch( strtolower($arg) ) { | |
case "full": | |
full(); | |
break; | |
case "incremental": | |
incremental(); | |
break; | |
case "prepare": | |
prepare(); | |
break; | |
case "restore": | |
// This is dangerous, yo. Gotta think of a good way to fool-proof it. | |
restore(); | |
break; | |
case "reset": | |
exec("rm -rf {$backup_dir}/logs/* {$backup_dir}/incremental/* {$backup_dir}/full/* {$backup_dir}/last_incremental"); | |
echo "Backup directory cleaned.\n"; | |
break; | |
default: | |
echo "No option supplied. See usage below:\n\n"; | |
echo $usage."\n"; | |
exit(); | |
} | |
## Functions! | |
function full() { | |
global $user, $pass, $backup_dir, $base_args; | |
if( ! file_exists($backup_dir) ) { | |
echo "Cannot find backup directory : {$backup_dir}\n"; | |
exit(); | |
} | |
if( file_exists($backup_dir."/full") ) { | |
echo "Found existing full backup directory. Removing it and all incrementals.\n"; | |
exec("rm -rf {$backup_dir}/full {$backup_dir}/incremental/* {$backup_dir}/last_incremental"); | |
} | |
echo "Starting backup -- this takes an average of 3 to 3.5 hours.\n"; | |
exec("innobackupex {$base_args} --no-timestamp {$backup_dir}/full &>{$backup_dir}/logs/full.log"); | |
if( check_log($backup_dir."/logs/full.log") ) { | |
echo "Backup complete.\n"; | |
exec("rm -f {$backup_dir}/logs/full.log"); | |
} else { | |
echo "Backup failed. Review {$backup_dir}/logs/full.log\n"; | |
} | |
} | |
function incremental() { | |
global $user, $pass, $backup_dir, $base_args; | |
echo "Starting incremental backup.\n"; | |
echo "Checking for existing full backup.\n"; | |
if( ! file_exists($backup_dir."/full/xtrabackup_checkpoints") ) { | |
echo "There is no full backup. Please execute: inno_backup full\n"; | |
exit(); | |
} | |
echo "Found existing full backup. Kicking off incremental backup.\n"; | |
if( ! file_exists($backup_dir."/last_incremental") ) { | |
$inc = 1; | |
file_put_contents($backup_dir."/last_incremental", $inc); | |
} else { | |
echo "Found last_incremental file\n"; | |
$inc = (int) file_get_contents($backup_dir."/last_incremental"); | |
echo "Last incremental: #{$inc}\n"; | |
$inc++; | |
} | |
if( $inc == 1 ) { | |
exec("innobackupex {$base_args} --no-timestamp --incremental {$backup_dir}/incremental/{$inc} --incremental-basedir={$backup_dir}/full &>{$backup_dir}/logs/incremental.log"); | |
} else { | |
$inc_prev = $inc-1; | |
exec("innobackupex {$base_args} --no-timestamp --incremental {$backup_dir}/incremental/{$inc} --incremental-basedir={$backup_dir}/incremental/{$inc_prev} &>{$backup_dir}/logs/incremental.log"); | |
} | |
if( check_log($backup_dir."/logs/incremental.log") ) { | |
echo "Incremental complete.\n"; | |
exec("rm -f {$backup_dir}/logs/incremental.log"); | |
} else { | |
echo "Backup failed. Review {$backup_dir}/logs/incremental.log\n"; | |
exit(); | |
} | |
file_put_contents($backup_dir."/last_incremental", $inc++); | |
} | |
function prepare() { | |
global $user, $pass, $backup_dir, $base_args; | |
if( ! file_exists($backup_dir."/last_incremental") ) { | |
exit("Missing {$backup_dir}/last_incremental.\n"); | |
} | |
echo "Rebuilding full backup from incrementals for restoration. This may take a while ...\n"; | |
$inc = (int) file_get_contents($backup_dir."/last_incremental"); | |
exec("innobackupex {$base_args} --no-timestamp --use-memory=500M --apply-log {$backup_dir}/full &>{$backup_dir}/logs/prepare.log"); | |
if( ! check_log($backup_dir."/logs/prepare.log") ) { | |
echo "Failed to prepare backup for restoration. Check {$backup_dir}/logs/prepare.log\n"; | |
exit(); | |
} | |
for($i = 1; $i <= $inc; $i++) { | |
echo "Processing incremental: {$i}\n"; | |
exec("innobackupex {$base_args} --apply-log --redo-only --incremental /backups/full/ --incremental-basedir=/backups/incremental/{$i} &>{$backup_dir}/logs/prepare.log"); | |
if( ! check_log($backup_dir."/logs/prepare.log") ) { | |
echo "Failed to apply incremental #{$i} to full backup. Check {$backup_dir}/logs/prepare.log\n"; | |
exit(); | |
} | |
} | |
echo "Final preparation running ...\n"; | |
exec("innobackupex {$base_args} --apply-log {$backup_dir}/full --use-memory=500M &>{$backup_dir}/logs/prepare.log"); | |
if( check_log($backup_dir."/logs/prepare.log") ) { | |
echo "Preparation has completed successfully. You may now attempt: inno_backup restore\n"; | |
} else { | |
exit("Final preparation has failed. Review {$backup_dir}/logs/prepare.log\n"); | |
exec("rm -rf {$backup_dir}/last_incremental {$backup_dir}/incremental/* {$backup_dir}/logs/prepare.log"); | |
} | |
} | |
function restore() { | |
global $user, $pass, $backup_dir, $base_args; | |
echo "This is a highly destructive process. Do not run this unless absolutely sure.\n"; | |
echo "Are you sure you want to run the restore process? (y/N): "; | |
$handle = fopen("php://stdin", "r"); | |
$line = fgets($handle); | |
if( strtolower(trim($line)) != 'y' ) { | |
exit("Aborting. Good choice, mortal.\n"); | |
} | |
echo "Just kidding. This isn't implemented yet, meat-bag.\n"; | |
} | |
function check_log($file) { | |
$res = exec("tail -n 1 {$file}"); | |
if( strstr($res, "OK!" ) ) { | |
return true; | |
} | |
return false; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment