#!/usr/bin/perl # make_multiple_sandbox # The MySQL Sandbox # Copyright (C) 2006-2010 Giuseppe Maxia # Contacts: http://datacharmer.org # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; version 2 of the License # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA use strict; use warnings; use English qw( -no_match_vars ); use Data::Dumper; use Getopt::Long qw(:config no_ignore_case ); use MySQL::Sandbox qw(get_ranges runs_as_root use_env sbinstr); use MySQL::Sandbox::Scripts; my $DEBUG = $MySQL::Sandbox::DEBUG; runs_as_root(); unless ( -d $ENV{'SANDBOX_HOME'} ) { mkdir $ENV{'SANDBOX_HOME'} or die "can't create $ENV{'SANDBOX_HOME'} ($CHILD_ERROR) \n"; } #my $install_dir; #$install_dir = $PROGRAM_NAME; #$install_dir =~ s{/\w+(\.pl)?$}{}; #eval "use lib q($install_dir)"; #eval "use MySandbox"; #eval "use MyScripts"; my $msb = MySQL::Sandbox->new(); my %defaults = ( circular_base_port => $MySQL::Sandbox::default_base_port{multiple},, group_directory => 'multi_msb', ); $msb->parse_options ( MySQL::Sandbox::Scripts::parse_options_many() ); # my %{$msb->{options}} = map { $_ , $msb->{parse_options}{$_}{'value'}} keys %{$msb->{parse_options}} ; GetOptions ( map { $msb->{parse_options}{$_}{parse}, \$msb->{options}{$_} } grep { $msb->{parse_options}{$_}{parse}} keys %{$msb->{parse_options}} ) or $msb->get_help(); $msb->get_help() if $msb->{options}{'help'}; unless ($msb->{options}{server_version}) { $msb->{options}{server_version} = shift or $msb->get_help('server version required'); } for my $opt (qw(repl_user repl_password remote_access)) { if (defined $msb->{options}{$opt}) { $MySQL::Sandbox::default_users{$opt} = $msb->{options}{$opt}; } } if ($msb->{options}{node_options}) { $ENV{NODE_OPTIONS} = $msb->{options}{node_options}; } if ($msb->{options}{one_node_options}) { for my $node_opt (@{ $msb->{options}{one_node_options}} ) { if ($node_opt =~ /^(\d+):\s*(\S.+)/) { $ENV{"NODE${1}_OPTIONS"} = $2; } else { get_help("invalid format for --one_node_option ($node_opt)"); } } } my $base_version ; my $release_no; my $dashed_version ; my $replication_port = 0; my $temp_path = $msb->{options}{server_version}; $temp_path =~ s{.*/}{}; my $prefix = ''; if ( $temp_path =~ /^(\D+)\d+/ ) { $prefix = $1; } if ( $temp_path =~ /(\d+)\.(\d+)\.(\d+)/ ) { $base_version = "$1$2$3"; $release_no = $3; $dashed_version = "${prefix}$1_$2_$3"; $base_version =~ s/^0+//; } else { $msb->get_help("No recognizable version pattern in $msb->{options}{server_version}\n"); } #print Dumper(\%{$msb->{options}}); my $group_directory ; if ( $msb->{options}{group_directory}) { $group_directory = "$msb->{options}{upper_directory}/$msb->{options}{group_directory}"; } else { $group_directory = "$msb->{options}{upper_directory}/$defaults{group_directory}_$dashed_version"; } if ( -d $group_directory ) { if ( -x "$group_directory/clear_all" ) { system "$group_directory/clear_all"; } system "rm -rf $group_directory/*"; } else { print "creating group directory $group_directory\n" if $msb->{options}{verbose}; mkdir $group_directory or die "can't create $group_directory\n"; } if ( -f "$group_directory/needs_initialization" ) { system "rm -f $group_directory/needs_initialization" ; } if ($msb->{options}{'master_master'}) { $msb->{options}{'how_many_nodes'} = 2; $msb->{options}{'circular'} = 1; } for my $dir ( 1 .. $msb->{options}{how_many_nodes}) { if ( -d "$group_directory/node$dir" ) { system "rm -rf $group_directory/node$dir" and die "unable to clean $group_directory/node$dir\n"; } } if ( $msb->{options}{sandbox_base_port}) { $replication_port = $msb->{options}{sandbox_base_port}; } else { $replication_port = $defaults{circular_base_port} + $base_version + ($release_no * 100); } if ( $msb->{options}{check_base_port}) { $replication_port = get_ranges({ min_range => $replication_port, range_size => $msb->{options}{how_many_nodes}, max_range => 64000, search_path => $ENV{SANDBOX_HOME}, }, 1); } my $circular_replication_script = "$group_directory/set_circular_replication.sh"; if ($msb->{options}{'circular'}) { $msb->write_to($circular_replication_script, '>', qq( #!/bin/bash TMPL1="change master to master_host='127.0.0.1'," TMPL2="master_user='$MySQL::Sandbox::default_users{'repl_user'}'," TMPL3="master_password='$MySQL::Sandbox::default_users{'repl_password'}'," TMPL4="master_port" TMPL="\$TMPL1 \$TMPL2 \$TMPL3 \$TMPL4" )); $msb->write_to($circular_replication_script, '>>', "$group_directory/use_all 'stop slave' "); } for my $node (1 .. $msb->{options}{how_many_nodes}) { my $additional_node_options = $ENV{NODE_OPTIONS} || ''; my $this_node_options = $ENV{"NODE${node}_OPTIONS"} || ''; my $node_port = $replication_port+ $node; if ($msb->{options}{'circular'}) { my $master_node_port = $node == 1 ? $node_port + $msb->{options}{how_many_nodes} -1 : $node_port -1; $additional_node_options .= '-c log-slave-updates ' . '-c replicate-same-server-id=0 ' . '-c auto_increment_increment=' . $msb->{options}{'how_many_nodes'} . ' ' . '-c auto_increment_offset=' . $node . ' ' . '-c report-host=SBnode' . $node . ' ' . "-c report-port=$node_port" ; } $additional_node_options .= q{ }; my $node_id = 100 + $node; print "installing node $node\n"; my $install_node = ''; my $install_node_command = qq( make_sandbox $msb->{options}{server_version} -- \\ --datadir_from=script \\ --upper_directory=$group_directory \\ --sandbox_directory=node$node \\ --no_confirm \\ --no_check_port \\ --repl_user=$msb->{options}{repl_user} \\ --repl_password=$msb->{options}{repl_password} \\ --remote_access=$msb->{options}{remote_access} \\ --prompt_prefix=node$node \\ --sandbox_port=$node_port \\ -c server-id=$node_id \\ -c log-bin=mysql-bin $additional_node_options $this_node_options ); if ($msb->{options}{interactive} or ($additional_node_options =~ /\binteractive\b/)) { system($install_node_command); } else { $install_node = qx($install_node_command); } print $install_node if $msb->{options}{verbose}; if ($CHILD_ERROR) { print "error installing node $node\n"; print "$install_node\n($CHILD_ERROR $OS_ERROR)\n"; exit(1) } #sleep 1; if ($msb->{options}{'circular'}) { my $master_node = $node + 1 ; if ($master_node > $msb->{options}{'how_many_nodes'} ) { $master_node = 1; } $msb->write_to($circular_replication_script, '>>', sprintf(q(echo "$TMPL=%d" | %s/node%d/use -u root ), $node_port, $group_directory, $master_node ) ); } } if ($msb->{options}{'circular'}) { $msb->write_to($circular_replication_script, '>>', "$group_directory/use_all 'start slave' "); chmod 0755, $circular_replication_script; } my $current_dir = $ENV{PWD}; chdir $group_directory; for my $cmd ( qw(start stop clear send_kill ) ) { my $cmd_fname = $cmd . '_all'; $msb->write_to($cmd_fname, '>', '#!/bin/sh'); if ($cmd =~ /stop|clear|send_kill/ && $msb->{options}{'circular'}) { $msb->write_to ($cmd_fname, '>>', qq($group_directory/use_all "stop slave")); } for my $node (1 .. $msb->{options}{how_many_nodes} ) { $msb->write_to ($cmd_fname, '>>', qq(echo 'executing "$cmd" on node $node')); $msb->write_to ($cmd_fname, '>>', qq($group_directory/node$node/$cmd "\$@")); if (($cmd eq 'start') and $msb->{options}{'circular'}) { $msb->write_to ($cmd_fname, '>>', "if [ -f $group_directory/node$node/data/master.info ] \n" . "then\n" . " echo 'stop slave' | $group_directory/n$node -u root\n" . "fi"); } } chmod 0755, $cmd_fname; } $msb->write_to('restart_all', '>', '#!/bin/sh'); $msb->write_to('restart_all', '>>', qq($group_directory/stop_all)); $msb->write_to('restart_all', '>>', qq($group_directory/start_all "\$@")); chmod 0755, 'restart_all'; $msb->write_to('use_all', '>', '#!/bin/sh'); my $node_list = join(' ', (1 .. $msb->{options}{how_many_nodes}) ); $msb->write_to('use_all', '>>', 'if [ "$1 " = " " ]'); $msb->write_to('use_all', '>>', 'then'); $msb->write_to('use_all', '>>', ' echo "syntax: $0 command"'); $msb->write_to('use_all', '>>', ' exit 1'); $msb->write_to('use_all', '>>', 'fi'); $msb->write_to('use_all', '>>', ''); $msb->write_to('use_all', '>>', 'for SNUM in ' . $node_list); $msb->write_to('use_all', '>>', 'do ' ); $msb->write_to('use_all', '>>', ' echo "# server: $SNUM: " ' ); $msb->write_to('use_all', '>>', ' echo "$@" | ' . $group_directory . '/node$SNUM/use $MYCLIENT_OPTIONS '); $msb->write_to('use_all', '>>', 'done ' ); chmod 0755, 'use_all'; $msb->write_to('status_all', '>', '#!/bin/sh'); my $gdir = $group_directory; $gdir =~ s{.*/}{}; my $group_type = 'MULTIPLE'; if ($msb->{options}{'circular'}) { $group_type = 'CIRC-REPL'; } $msb->write_to('status_all', '>>', "echo $group_type $gdir" ); $msb->write_to('status_all', '>>', 'for SNUM in ' . $node_list); $msb->write_to('status_all', '>>', 'do ' ); $msb->write_to('status_all', '>>', $group_directory . '/node$SNUM/status'); $msb->write_to('status_all', '>>', 'done ' ); chmod 0755, 'status_all'; $msb->write_to('check_slaves', '>', '#!/bin/sh' ); $msb->write_to('check_slaves', '>>', 'for NNUM in ' . $node_list ); $msb->write_to('check_slaves', '>>', 'do' ); $msb->write_to('check_slaves', '>>', ' echo "node # $NNUM"'); $msb->write_to('check_slaves', '>>', " $group_directory/node\$NNUM/use -N -e " . " 'show slave status\\G' | grep Running:" ); $msb->write_to('check_slaves', '>>', 'done ' ); chmod 0755, 'check_slaves'; for my $node (1 .. $msb->{options}{how_many_nodes} ) { $msb->write_to("n$node", '>', "#!/bin/sh"); $msb->write_to("n$node", '>', qq($group_directory/node$node/use "\$@")); chmod 0755, "n$node"; } if ($msb->{options}{'circular'}) { $msb->write_to( "$group_directory/clear_all", '>>', "date > $group_directory/needs_initialization"); $msb->write_to(qq($group_directory/start_all "\$@"), '>>', "if [ -f $group_directory/needs_initialization ] \n" . "then\n" . " $circular_replication_script\n" . " rm -f $group_directory/needs_initialization\n" . "else\n" . " $group_directory/use_all 'start slave' \n" . "fi"); # # Creates the replication user in all nodes # ## system qq( MYCLIENT_OPTIONS='-u root' ./use_all 'grant replication slave on *.* ) ## . qq( to "$MySQL::Sandbox::default_users{'repl_user'}"\@"127.%" ) . ## . qq(identified by "$MySQL::Sandbox::default_users{'repl_password'}"') ## and die "can't create slave user"; my $result = system($circular_replication_script); if ($result) { die ("error starting up circular replication ($!)\n"); } else { print "Circular replication activated\n"; } } chdir $current_dir; print "group directory installed in ", use_env($group_directory),"\n"; sbinstr( "group_directory installed in <" . use_env($group_directory) . ">"); __END__