Skip to content

Instantly share code, notes, and snippets.

@gwinans
Created July 27, 2016 17:51
Show Gist options
  • Save gwinans/96bb813c847a2ffce69ba0dafd5c39bf to your computer and use it in GitHub Desktop.
Save gwinans/96bb813c847a2ffce69ba0dafd5c39bf to your computer and use it in GitHub Desktop.
#!/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