Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Tree: fb9cd253a2
Fetching contributors…

Cannot retrieve contributors at this time

executable file 260 lines (219 sloc) 7.138 kB
#!/usr/bin/perl
use strict;
use warnings;
use DBI;
use Getopt::Long;
use Sys::HostAddr qw//;
use Net::CIDR::Lite;
sub _log {
my $log = ( @_ > 1 ) ? sprintf shift,@_ : $_[0];
my @lt = localtime;
my $lt = sprintf '%04d-%02d-%02dT%02d:%02d:%02d', $lt[5]+1900,$lt[4]+1,$lt[3],$lt[2],$lt[1],$lt[0];
print "-- [$lt] $log\n";
warn "[$lt] $log\n";
}
sub _wlog {
my $log = ( @_ > 1 ) ? sprintf shift,@_ : $_[0];
my @lt = localtime;
my $lt = sprintf '%04d-%02d-%02dT%02d:%02d:%02d', $lt[5]+1900,$lt[4]+1,$lt[3],$lt[2],$lt[1],$lt[0];
warn "[$lt] $log\n";
}
sub lc_key {
my $hashref = shift;
my %hash;
$hash{lc($_)}=$hashref->{$_} for keys %$hashref;
return \%hash;
}
sub get_local_ip {
my $cidr = Net::CIDR::Lite->new;
$cidr->add_any('10.0.0.0/8');
$cidr->add_any('127.16.0.1/12');
$cidr->add_any('192.168.0.0/16');
my $sysaddr = Sys::HostAddr->new( ipv => 4 );
my $main_ip = $sysaddr->main_ip;
if ( !$main_ip || !$cidr->find($main_ip) ) {
my $ip = first { $cidr->find($_) } @{$sysaddr->addresses};
$main_ip = $ip if $ip;
}
return $main_ip;
}
sub get_master_status {
my $dbh = shift;
my %master = @_;
my $sth = $dbh->prepare('SHOW MASTER STATUS');
$sth->execute;
my $repl_status = lc_key($sth->fetchrow_hashref('NAME'));
die "Died: couldnot get master status" unless $repl_status->{file};
if ( !$master{port} ) {
my $variables = $dbh->selectrow_arrayref(q!SHOW VARIABLES LIKE 'port'!, {});
$master{port} = $variables->[1];
}
$master{port} ||= 3306;
$master{host} ||= get_local_ip();
die "Died: couldnot get own ipaddr" unless $master{host};
return sprintf "CHANGE MASTER TO MASTER_LOG_FILE='%s', MASTER_LOG_POS=%s, MASTER_HOST='%s', MASTER_PORT=%s, MASTER_USER='%s', MASTER_PASSWORD='%s';",
$repl_status->{file},
$repl_status->{position},
$master{host},
$master{port},
$master{user},
$master{password};
}
sub get_slave_status {
my $dbh = shift;
my %master = @_;
my $sth = $dbh->prepare('SHOW SLAVE STATUS');
$sth->execute;
my $repl_status = lc_key($sth->fetchrow_hashref('NAME'));
return unless $repl_status->{relay_master_log_file};
return sprintf "CHANGE MASTER TO MASTER_LOG_FILE='%s', MASTER_LOG_POS=%s, MASTER_HOST='%s', MASTER_PORT=%s, MASTER_USER='%s', MASTER_PASSWORD='%s';",
$repl_status->{relay_master_log_file},
$repl_status->{exec_master_log_pos},
$master{host} || $repl_status->{master_host},
$master{port} || $repl_status->{master_port},
$master{user} || $repl_status->{master_user},
$master{password};
}
my %master;
Getopt::Long::Configure ("no_ignore_case");
GetOptions(
"master" => \my $master,
"slave" => \my $slave,
"repl" => \my $repl,
"host=s" => \my $host,
"port=s" => \my $port,
"user=s" => \my $user,
"password=s" => \my $password,
"master-user=s" => \$master{user},
"master-password=s" => \$master{password},
"master-host=s" => \$master{host},
"master-port=s" => \$master{port},
"h|help" => \my $help,
);
if ( $help ) {
_wlog("usage: $0 (--master|--slave) (--repl) --master-user 'repl_username' --master-password 'repl_user_password' -(--master-ip ipaddr) (--master-port port) -- [database,[database]...]");
exit(1);
}
if ( $master && $slave ) {
_wlog("$0 --master and $0 --slave is exclusive");
exit(1);
}
if ( !$master && !$slave ) {
_wlog("$0 --master or $0 --slave is needed");
exit(1);
}
if ( $master && !$master{user} ) {
_wlog("$0 --master-user is needed with master mode");
exit(1);
}
if ( !$master{password} ) {
_wlog("$0 --master-password is needed");
exit(1);
}
my @databases = @ARGV;
$|=1;
my @mysqldump = ('/usr/bin/mysqldump','/usr/local/bin/mysqldump','/usr/local/mysql/bin/mysqldump');
my $mysqldump;
for ( @mysqldump ) {
if ( -x $_ ) {
$mysqldump = $_;
last;
}
}
die "Died: couldnot find mysqldump" unless $mysqldump;
$host ||= 'localhost';
$port ||= 3306;
$user ||= 'root';
my $dbh = DBI->connect_cached('dbi:mysql:mysql;host='.$host.';port='.$port, $user, $password, {
RaiseError => 1,
PrintError => 0,
ShowErrorStatement => 1,
});
_log("mysql40dump start");
my ($version) = @{$dbh->selectrow_arrayref('SELECT VERSION()', {})};
my $type = ( $version =~ m!^4! ) ? 'Type' : 'Engine';
$dbh->do('DROP TABLE IF EXISTS tmp_backup_dummy');
$dbh->do("CREATE TABLE tmp_backup_dummy(a INT) $type=InnoDB");
$dbh->{'AutoCommit'} = 0;
$dbh->do('INSERT INTO tmp_backup_dummy VALUES (?)',undef,1);
$dbh->do('FLUSH TABLES WITH READ LOCK');
$dbh->commit;
_log('Done "FLUSH TABLES WITH READ LOCK"');
if (!@databases) {
my $sth = $dbh->prepare('SHOW DATABASES');
$sth->execute();
while ( my $ret = $sth->fetchrow_arrayref ) {
push @databases, $ret->[0] if $ret->[0] !~ /^(?:information_schema|performance_schema|mysql|test)$/;
}
}
my $master_status = get_master_status($dbh,%master);
my $slave_status = get_slave_status($dbh,%master);
my $change_master_st;
if ( $master ) {
die "cannot get 'MASTER STATUS'" unless $master_status;
$change_master_st = $master_status;
$slave_status = 'not configured' unless $slave_status;
}
elsif ( $slave ) {
die "cannot get 'SLAVE STATUS'" unless $slave_status;
$change_master_st = $slave_status;
$master_status = 'not configured' unless $master_status;
}
my $master_mode = $master ? ' * ' : ' ';
my $slave_mode = $slave ? ' * ' : ' ';
_log("[FROM MASTER STATUS]$master_mode$master_status");
_log("[FROM SLAVE STATUS]$slave_mode$slave_status");
_log("[START] mysqldump --host $host --port $port --quick --add-locks --extended-insert --single-transaction --databases " . join(" ", @databases));
print "set FOREIGN_KEY_CHECKS=0;\n" if $version =~ m!^4!;
pipe my $logrh, my $logwh
or die "Died: failed to create pipe:$!";
my $pid = fork;
if ( ! defined $pid ) {
die "Died: fork failed: $!";
}
elsif ( $pid == 0 ) {
#child
$dbh->STORE(InactiveDestroy => 1);
close $logrh;
open STDOUT, '>&', $logwh
or die "Died: failed to redirect STDOUT";
close $logwh;
exec(
$mysqldump,
'--host',
$host,
'--port',
$port,
'--quick',
'--add-locks',
'--extended-insert',
'--single-transaction',
( $version =~ m!^5! ) ? ('--order-by-primary') : (),
'--databases',
@databases
);
die "Died: exec failed: $!";
}
#parent
close $logwh;
my $unlock=0;
while(<$logrh>){
print;
if ( $unlock == 0 && m!^CREATE DATABASE!) {
_wlog('Found first "CREATE DATABASE" statement. execute UNLOCK TABLES');
$dbh->do('UNLOCK TABLE');
$unlock++;
}
}
close $logrh;
while (wait == -1) {}
my $exit_code = $?;
if ( $exit_code != 0 ) {
_log("Error: mysqldump exited with code: %d", $exit_code >> 8);
}
if ( $exit_code == 0 && $repl ) {
print "$change_master_st\n";
print "START SLAVE;\n";
}
_log("mysql40dump ended ");
exit($exit_code >> 8);
Jump to Line
Something went wrong with that request. Please try again.