|
|
|
|
| 1 |
|
| 2 |
|
| 3 |
|
| 4 |
|
| 5 |
|
| 6 |
|
| 7 |
|
| 8 |
|
| 9 |
|
| 10 |
use strict; |
| 11 |
|
| 12 |
|
| 13 |
|
| 14 |
my $action; |
| 15 |
|
| 16 |
if(scalar(@ARGV) == 1 && $ARGV[0] eq "full") { |
| 17 |
$action = \&full_backup; |
| 18 |
} |
| 19 |
elsif(scalar(@ARGV) == 1 && $ARGV[0] eq "incremental") { |
| 20 |
$action = \&incremental_backup; |
| 21 |
} |
| 22 |
else { |
| 23 |
die("Usage: backup_database.pl [full|incremental]\n"); |
| 24 |
} |
| 25 |
|
| 26 |
|
| 27 |
my $username = ""; # mysql root username |
| 28 |
my $password = ""; # mysql root password |
| 29 |
my $database_match = "'[a-zA-Z0-9.]*lc\$'"; # regex to match mysql databases to be backed up |
| 30 |
my $mysql_database_location = "/var/lib/mysql"; # location of mysql databases in filesystem |
| 31 |
my $mysql_log_location = "/var/log/mysql"; |
| 32 |
my $log_directory = "/var/log/mysql_backup"; # where to keep the error log |
| 33 |
my $backup_directory = "/var/backups/mysqld"; # where to store the backups |
| 34 |
|
| 35 |
my $datestamp = time_stamp(); |
| 36 |
my $errors = 0; |
| 37 |
|
| 38 |
|
| 39 |
print "Beginning $ARGV[0] mysqld backup ($datestamp)...\n"; |
| 40 |
|
| 41 |
|
| 42 |
`echo "$datestamp beginning backup..." >> ${log_directory}/mysql_backup_error.log`; |
| 43 |
|
| 44 |
|
| 45 |
my @database_list = `ls ${mysql_database_location} -1 | grep $database_match 2>> ${log_directory}/mysql_backup_error.log`; |
| 46 |
|
| 47 |
|
| 48 |
foreach my $current_database (@database_list) { |
| 49 |
|
| 50 |
chomp($current_database); |
| 51 |
|
| 52 |
|
| 53 |
$errors += $action->($current_database); |
| 54 |
|
| 55 |
} |
| 56 |
|
| 57 |
|
| 58 |
print "Backup Complete. There were $errors errors.\n\n"; |
| 59 |
|
| 60 |
|
| 61 |
`echo "\Backup complete\n\n" >> ${log_directory}/mysql_backup_error.log`; |
| 62 |
|
| 63 |
|
| 64 |
sub full_backup($) { |
| 65 |
|
| 66 |
my $current_database = shift(@_); |
| 67 |
my $errors = 0; |
| 68 |
|
| 69 |
|
| 70 |
mkdir("${backup_directory}/${current_database}", 0755); |
| 71 |
|
| 72 |
|
| 73 |
|
| 74 |
|
| 75 |
my $sed_string = $backup_directory . "/" . $current_database . "/"; |
| 76 |
|
| 77 |
|
| 78 |
$sed_string =~ s/\//\\\//g; |
| 79 |
|
| 80 |
my @old_files = `ls ${backup_directory}/${current_database}/ -1 2> /dev/null | sed 's/\\(.*\\)/${sed_string}\\1/'`; |
| 81 |
chomp(@old_files); |
| 82 |
|
| 83 |
|
| 84 |
|
| 85 |
print "\tCreating full backup of database: $current_database... "; |
| 86 |
`mysqldump --databases --single-transaction --user=${username} --password=${password} --master-data=2 ${current_database} > "${backup_directory}/${current_database}/${current_database}-full-mysqld-backup-${datestamp}.sql" 2>> ${log_directory}/mysql_backup_error.log`; |
| 87 |
|
| 88 |
|
| 89 |
if(($? & 255) != 0) { |
| 90 |
print "FAILED (mysqldump error code " . ($? & 255) . ")\n"; |
| 91 |
# Remove the failed backup |
| 92 |
unlink ("${backup_directory}/${current_database}/${current_database}-full-mysqld-backup-${datestamp}.sql"); |
| 93 |
print "\tNone of your existing backup files have been touched\n"; |
| 94 |
$errors++; |
| 95 |
} |
| 96 |
else { |
| 97 |
print "SUCCESSFUL\n"; |
| 98 |
# Remove all of the old backups, now that we have a fresh, up-to-date backup. |
| 99 |
unlink(@old_files); |
| 100 |
print "\tOld backups have been purged from ${backup_directory}/$current_database.\n"; |
| 101 |
|
| 102 |
} |
| 103 |
|
| 104 |
return $errors; |
| 105 |
} |
| 106 |
|
| 107 |
|
| 108 |
sub incremental_backup($) { |
| 109 |
|
| 110 |
my $current_database = shift(@_); |
| 111 |
my $errors = 0; |
| 112 |
|
| 113 |
print "\tDatabase: $current_database\n"; |
| 114 |
print "\t\tFinding the current full backup of $current_database... "; |
| 115 |
|
| 116 |
|
| 117 |
|
| 118 |
|
| 119 |
my ($full_backup) = `ls ${backup_directory}/${current_database} -1r 2>> ${log_directory}/mysql_backup_error.log | grep "${current_database}-full-mysqld-backup-[0-9]*\.sql\$" 2>> ${log_directory}/mysql_backup_error.log`; |
| 120 |
my $ret_ls = $?; |
| 121 |
|
| 122 |
chomp($full_backup); |
| 123 |
|
| 124 |
|
| 125 |
my $ret_open = open(FULL_BACKUP, "${backup_directory}/${current_database}/${full_backup}"); |
| 126 |
|
| 127 |
|
| 128 |
|
| 129 |
|
| 130 |
if(($ret_ls & 255) || !$ret_open) { |
| 131 |
print "FAILED "; |
| 132 |
if($ret_ls & 255 || $full_backup eq "") { |
| 133 |
print "(Could not find the last full backup)\n\t"; |
| 134 |
|
| 135 |
# Try to create a full backup... |
| 136 |
my $create_backup_errors = full_backup($current_database); |
| 137 |
|
| 138 |
# If this fails too, give up. Otherwise, keep going as usual |
| 139 |
if($create_backup_errors) { |
| 140 |
$errors += $create_backup_errors; |
| 141 |
return $errors; |
| 142 |
} |
| 143 |
else { |
| 144 |
return 0; |
| 145 |
} |
| 146 |
|
| 147 |
} |
| 148 |
else { |
| 149 |
print "(Could not open file: ${backup_directory}/${current_database}/$full_backup)\n"; |
| 150 |
print "\t\tAborting backup of database: $current_database\n"; |
| 151 |
return ++$errors; |
| 152 |
} |
| 153 |
} |
| 154 |
else { |
| 155 |
print "SUCCESSFUL ($full_backup)\n"; |
| 156 |
} |
| 157 |
|
| 158 |
|
| 159 |
my $master_log_file = -1; |
| 160 |
my $master_log_position = -1; |
| 161 |
|
| 162 |
print "\t\tFinding the last recorded transaction... "; |
| 163 |
|
| 164 |
# Search through the full backup until you find the reference to the log file |
| 165 |
|
| 166 |
while(my $line = <FULL_BACKUP>) { |
| 167 |
|
| 168 |
if($line =~ m/MASTER_LOG_FILE='(mysql-bin.\d*)', MASTER_LOG_POS=(\d*);/) { |
| 169 |
$master_log_file = $1; |
| 170 |
$master_log_position = $2; |
| 171 |
last; |
| 172 |
} |
| 173 |
} |
| 174 |
|
| 175 |
|
| 176 |
if($master_log_file == -1 || $master_log_position == -1) { |
| 177 |
print "FAILED\n\t\tAborting backup of database: $current_database\n"; |
| 178 |
return ++$errors; |
| 179 |
} |
| 180 |
else { |
| 181 |
print "SUCCESSFUL\n"; |
| 182 |
} |
| 183 |
|
| 184 |
print "\t\tCreating the incremental backup for $current_database... "; |
| 185 |
|
| 186 |
|
| 187 |
|
| 188 |
`mysqlbinlog --database=${current_database} --start-position=${master_log_position} --to-last-log --result-file=${backup_directory}/${current_database}/${current_database}-incremental-mysqld-backup-${datestamp}.sql ${mysql_log_location}/${master_log_file} 2>>${log_directory}/mysql_backup_error.log`; |
| 189 |
|
| 190 |
|
| 191 |
if(($? & 255) != 0) { |
| 192 |
print "FAILED (mysqlbinlog error code: " . ($? & 255) . ")\n"; |
| 193 |
$errors++; |
| 194 |
} |
| 195 |
else { |
| 196 |
print "SUCCESSFUL\n"; |
| 197 |
} |
| 198 |
|
| 199 |
return $errors; |
| 200 |
} |
| 201 |
|
| 202 |
|
| 203 |
sub time_stamp { |
| 204 |
my ($d); |
| 205 |
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); |
| 206 |
$year += 1900; |
| 207 |
$mon++; |
| 208 |
$d = sprintf("%4d%2.2d%2.2d%2.2d%2.2d%2.2d",$year,$mon,$mday,$hour,$min,$sec); |
| 209 |
return($d); |
| 210 |
} |
| 211 |
|
| 212 |
|