Sun Jul 25 04:03:01 PDT 2004
- Previous message: [Slony1-commit] By cbbrowne: New Directory
- Next message: [Slony1-commit] By darcyb: Work around braindead m4 processors on some OS's also to be
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Log Message: ----------- Add in a "first draft" of a set of Perl tools for configuring Slony-I instances that work by generating Slonik scripts. They do not use any specialized PostgreSQL modules, so as to maximize the chance that they will run portably even on commercial Un*xes where it may be impractical to add things like Pg or DBD:PG. Added Files: ----------- slony1-engine/tools/altperl: README (r1.1) ToDo (r1.1) create_set.pl (r1.1) drop_node.pl (r1.1) drop_set.pl (r1.1) failover.pl (r1.1) init_cluster.pl (r1.1) merge_sets.pl (r1.1) move_set.pl (r1.1) reset_cluster.pl (r1.1) restart_node.pl (r1.1) slon-tools.pm (r1.1) slon.env (r1.1) slon_kill.pl (r1.1) slon_pushsql.pl (r1.1) slon_start.pl (r1.1) slon_watchdog.pl (r1.1) subscribe_set.pl (r1.1) uninstall_node.pl (r1.1) unsubscribe_set.pl (r1.1) update_node.pl (r1.1) -------------- next part -------------- --- /dev/null +++ tools/altperl/merge_sets.pl @@ -0,0 +1,47 @@ +#!/usr/bin/perl +# $Id: merge_sets.pl,v 1.1 2004/07/25 04:02:50 cbbrowne Exp $ +# Author: Christopher Browne +# Copyright 2004 Afilias Canada + +require 'slon-tools.pm'; +require 'slon.env'; + +my ($node, $set1, $set2) = @ARGV; +if ($node =~ /^node(\d+)$/) { + # Set name is in proper form + $node = $1; +} else { + print "Valid node names are node1, node2, ...\n\n"; + die "Usage: ./merge_sets.pl nodeN setOLD setNEW\n"; +} + +if ($set1 =~ /^node(\d+)$/) { + $set1 = $1; +} else { + print "Valid set names are set1, set2, ...\n\n"; + die "Usage: ./merge_sets.pl nodeN setOLD setNEW\n"; +} +if ($set2 =~ /^node(\d+)$/) { + $set2 = $1; +} else { + print "Valid set names are set1, set2, ...\n\n"; + die "Usage: ./merge_sets.pl nodeN setOLD setNEW\n"; +} + +open(SLONIK, ">/tmp/slonik.$$"); +print SLONIK genheader(); +my ($dbname, $dbhost)=($DBNAME[1], $HOST[1]); +print SLONIK qq[ + try { + merge set (id = $set1, add id = $set2, origin = $node); + } + on error { + echo 'Failure to merge sets $set1 and $set2 with origin $node'; + exit 1; + } + echo 'Replication set $set2 merged in with $set1 on origin $node'; +]; + +close SLONIK; +`slonik < /tmp/slonik.$$`; +unlink("/tmp/slonik.$$"); --- /dev/null +++ tools/altperl/create_set.pl @@ -0,0 +1,111 @@ +#!/usr/bin/perl +# $Id: create_set.pl,v 1.1 2004/07/25 04:02:50 cbbrowne Exp $ +# Author: Christopher Browne +# Copyright 2004 Afilias Canada +require 'slon-tools.pm'; +require 'slon.env'; +my ($set) = @ARGV; +if ($set =~ /^set(\d+)$/) { + $set = $1; +} else { + print "Need set identifier\n"; + die "create_set.pl setN\n"; +} + +$OUTPUTFILE="/tmp/add_tables.$$"; + +open (OUTFILE, ">$OUTPUTFILE"); +print OUTFILE genheader(); + +foreach my $table (@SERIALTABLES) { + print OUTFILE " + echo ' Adding unique key to table public.$table...'; + table add key ( + node id=1, + full qualified name='public.$table' + ); +"; +} +close OUTFILE; +print `slonik < $OUTPUTFILE`; + +open (OUTFILE, ">$OUTPUTFILE"); +print OUTFILE genheader(); + +print OUTFILE " + try { + create set (id = $set, origin = 1, comment = 'Set for slony tables'); + } + on error { + echo 'Could not create subscription set!'; + exit -1; + } +"; + +close OUTFILE; +print `slonik < $OUTPUTFILE`; + +open (OUTFILE, ">$OUTPUTFILE"); +print OUTFILE genheader(); +print OUTFILE " + echo 'Subscription set created'; + echo 'Adding tables to the subscription set'; + +"; + +$TABLE_ID=1; +foreach my $table (@SERIALTABLES) { + if ($table =~ /^(.*\..*)$/) { + # Table has a namespace specified + } else { + $table = "public.$table"; + } + print OUTFILE " + set add table (set id = $set, origin = 1, id = $TABLE_ID, full qualified name = '$table', comment = 'Table public.$table', key=serial); + echo 'Add unkeyed table $table'; +"; + $TABLE_ID++; +} + +foreach my $table (@KEYEDTABLES) { + if ($table =~ /^(.*\..*)$/) { + # Table has a namespace specified + } else { + $table = "public.$table"; + } + print OUTFILE " + set add table (set id = $set, origin = 1, id = $TABLE_ID, full qualified name = '$table', comment = 'Table public.$table'); + echo 'Add keyed table $table'; +"; + $TABLE_ID++; +} + +close OUTFILE; +print `slonik < $OUTPUTFILE`; + +open (OUTFILE, ">$OUTPUTFILE"); +print OUTFILE genheader(); +# Finish subscription set... +print OUTFILE " + echo 'Adding sequences to the subscription set'; +"; + +$SEQID=1; +foreach my $seq (@SEQUENCES) { + if ($seq =~ /^(.*\..*)$/) { + # Table has a namespace specified + } else { + $seq = "public.$seq"; + } + print OUTFILE " + set add sequence (set id = $set, origin = 1, id = $SEQID, full qualified name = '$seq', comment = 'Sequence public.$seq'); + echo 'Add sequence $seq'; +"; + $SEQID++; +} +print OUTFILE " + echo 'All tables added'; +"; + +print `slonik < $OUTPUTFILE`; +unlink($OUTPUTFILE); --- /dev/null +++ tools/altperl/subscribe_set.pl @@ -0,0 +1,53 @@ +#!/usr/bin/perl +# $Id: subscribe_set.pl,v 1.1 2004/07/25 04:02:51 cbbrowne Exp $ +# Author: Christopher Browne +# Copyright 2004 Afilias Canada + +require 'slon-tools.pm'; +require 'slon.env'; +my ($set, $node) = @ARGV; +if ($node =~ /^node(\d+)$/) { + $node = $1; +} else { + print "Need to specify node!\n"; + die "subscribe_set setM nodeN\n"; +} + +if ($set =~ /^set(\d+)$/) { + $set = $1; +} else { + print "Need to specify set!\n"; + die "subscribe_set setM nodeN\n"; +} + +open(SLONIK, ">/tmp/slonik-subscribe.$$"); +print SLONIK genheader(); +print SLONIK "try {\n"; + +if ($DSN[$node]) { + my $parent = 1; + my $forward; + if ($PARENT[$node]) { + $parent = $PARENT[$node]; + } + if ($NOFORWARD[$node] eq "no") { + $forward = "no"; + } else { + $forward = "yes"; + } + print SLONIK " subscribe set (id = $set, provider = $parent, receiver = $node, forward = $forward);\n"; +} else { + die "Node $node not found\n"; +} + +print SLONIK "}\n"; +print SLONIK qq{ + on error { + exit 1; + } + echo 'Subscribed nodes to set 1'; +}; + +close SLONIK; +print `slonik < /tmp/slonik-subscribe.$$`; +unlink("/tmp/slonik-subscribe.$$"); --- /dev/null +++ tools/altperl/init_cluster.pl @@ -0,0 +1,206 @@ +#!/usr/bin/perl +# $Id: init_cluster.pl,v 1.1 2004/07/25 04:02:50 cbbrowne Exp $ +# Author: Christopher Browne +# Copyright 2004 Afilias Canada +my @COST; +my @PATH; + +require 'slon-tools.pm'; +require 'slon.env'; +my $FILE="init-cluster"; +open(SLONIK, ">/tmp/$FILE.$$"); + +print SLONIK genheader(); + +my ($dbname, $dbhost)=($DBNAME[1], $HOST[1]); +print SLONIK " +try { + init cluster (id = 1, comment = 'Node $dbname@$dbhost'); +"; + +foreach my $node (@NODES) { + if ($node > 1) { # skip the first one; it's already initialized! + my ($dbname, $dbhost) = ($DBNAME[$node], $HOST[$node]); + print SLONIK " store node (id = $node, comment = 'Node $dbname@$dbhost');\n"; + } +} + +print SLONIK "} on error { + echo 'Could not set up all nodes as slonik nodes'; + exit 1; +} +echo 'Set up replication nodes'; +"; +close SLONIK; + +`slonik < /tmp/$FILE.$$`; + +open(SLONIK, ">/tmp/$FILE.$$"); +print SLONIK genheader(); + +my @VIA ; +generate_listen_paths(); +report_on_paths(); +print SLONIK qq[ +echo 'Next: configure paths for each node/origin'; +]; +foreach my $nodea (@NODES) { + my $dsna = $DSN[$nodea]; + foreach my $nodeb (@NODES) { + if ($nodea != $nodeb) { + my $dsnb = $DSN[$nodeb]; + my $providerba = $VIA[$nodea][$nodeb]; + my $providerab = $VIA[$nodeb][$nodea]; + print SLONIK " store path (server = $nodea, client = $nodeb, conninfo = '$dsna');\n"; + print SLONIK " store path (server = $nodeb, client = $nodea, conninfo = '$dsnb');\n"; + print SLONIK "echo 'configured path between $nodea and $nodeb';\n"; + } + } +} + +close SLONIK; + +`slonik < /tmp/$FILE.$$`; + +open(SLONIK, ">/tmp/$FILE.$$"); +print SLONIK genheader(); + +foreach my $origin (@NODES) { + my $dsna = $DSN[$origin]; + foreach my $receiver (@NODES) { + if ($origin != $receiver) { + my $provider = $VIA[$origin][$receiver]; + print SLONIK " store listen (origin = $origin, receiver = $receiver, provider = $provider);\n"; + } + } +} + +print SLONIK qq[ + echo 'Replication nodes prepared'; + echo 'Please start the replication daemon on both systems'; +]; + +close SLONIK; +`slonik < /tmp/$FILE.$$`; +unlink("/tmp/$FILE.$$"); + +sub generate_listen_paths { + my @COST; + my @PATH; + + my $infinity = 10000000; # Initial costs are all infinite + foreach my $node1 (@NODES) { + foreach my $node2 (@NODES) { + $COST[$node1][$node2] = $infinity; + } + } + + # Initialize paths between parents and children, and based on them, + # generate initial seeding of listener paths, @VIA + + foreach my $node1 (@NODES) { + $COST[$node1][$node1] = 0; + $VIA[$node1][$node1] = 0; + foreach my $node2 (@NODES) { + if ($node2 != $node1) { + if ($PARENT[$node1] == $node2) { + $PATH[$node1][$node2] = 1; + $PATH[$node2][$node1] = 1; + # Set up a cost 1 path between them + # Parent to child + $COST[$node1][$node2] = 1; + $VIA[$node1][$node2] = $node1; + + # Child to parent + $COST[$node2][$node1] = 1; + $VIA[$node2][$node1] = $node2; + } + } + } + } + + # Now, update the listener paths... + # 4 level nested iteration: + # 1 while not done, do + # 2 for each node, node1 + # 3 for each node, node2, where node2 <> node1, where we don't + # yet have a listener path + # 4 for each node node3 (<> node1 or node2), + # consider introducing the listener path: + # node1 to node2 then node2 to node3 + # In concept, it's an O(n^4) algorithm; since the number of nodes, n, + # is not likely to get particularly large, it's not worth tuning + # further. + $didwork = "yes"; + while ($didwork eq "yes") { + $didwork = "no"; + foreach my $node1 (@NODES) { + foreach my $node3 (@NODES) { + if (($VIA[$node3][$node1] == 0) && ($node3 != $node1)) { + foreach my $node2 (@NODES) { + if ($PATH[$node1][$node2] && ($VIA[$node2][$node3] != 0) && ($node2 != $node3) && ($node2 != $node1)) { + # Consider introducing a path from n1 to n2 then n2 to n3 + # as a cheaper alternative to going direct from n1 to n3 + my $oldcost = $COST[$node3][$node1]; + my $newcost = $COST[$node1][$node2] + $COST[$node2][$node3]; + if ($newcost < $oldcost) { + $didwork = "yes"; + # So we go via node 2 + $VIA[$node3][$node1] = $node2; + $COST[$node3][$node1] = $newcost; + } + } + } + } + } + } + } +} + +sub report_on_paths { + print "cost\n"; + print " "; + foreach my $node2 (@NODES) { + printf "%4d|", $node2; + } + print "\n--------------------------------------------\n"; + foreach my $node1 (@NODES) { + printf "%4d|", $node1; + foreach my $node2 (@NODES) { + if ($COST[$node2][$node1] == $infinity) { + printf "inf "; + } else { + printf "%4d ", $COST[$node2][$node1]; + } + print "\n"; + } + } + print "\n\n"; + print "VIA\n"; + print " "; + foreach my $node2 (@NODES) { + printf "%4d|", $node2; + } + print "\n--------------------------------------------\n"; + foreach my $node1 (@NODES) { + printf "%4d", $node1; + foreach my $node2 (@NODES) { + printf "%4d ", $VIA[$node2][$node1]; + } + print "\n"; + } + + print "PATHS\n"; + print " "; + foreach my $node2 (@NODES) { + printf "%4d|", $node2; + } + print "\n--------------------------------------------\n"; + foreach my $node1 (@NODES) { + printf "%4d", $node1; + foreach my $node2 (@NODES) { + printf "%4d ", $PATH[$node2][$node1]; + } + print "\n"; + } +} --- /dev/null +++ tools/altperl/slon.env @@ -0,0 +1,126 @@ +# $Id: slon.env,v 1.1 2004/07/25 04:02:50 cbbrowne Exp $ +# Author: Christopher Browne +# Copyright 2004 Afilias Canada + +$SETNAME=flex2test; +$LOGDIR='/opt/logs/slon'; +$SLON_BIN_PATH='/opt/OXRS/dbs/pgsql74/bin'; + +add_node(host => 'marge', dbname=>'transtest', port=>5532, + user=>'postgres', password=>'postgres', node=>1); + +add_node(host => 'marge', dbname=>'transreplica', port=>5533, + user=>'postgres', password=>'postgres', node=>2, parent=>1); + +add_node(host => 'marge', dbname=>'transreplica', port=>5534, user=>'postgres', + password=>'postgres', node=>3, parent=>1); + +# add_node(host => 'marge', dbname=>'flexnodeb', port=>5532,user=>'postgres', +# password=>'postgres', node=>4, parent=>3); + +# add_node(host => 'marge', dbname=>'flexnodec', port=>5532,user=>'postgres', +# password=>'postgres', node=>5, parent=>4); + +# add_node(host => 'marge', dbname=>'flexnoded', port=>5532,user=>'postgres', +# password=>'postgres', node=>6, parent=>3); +# add_node(host => 'marge', dbname=>'flexnodee', port=>5532,user=>'postgres', +# password=>'postgres', node=>7, parent=>6, noforward=>'no'); + +# These are the tables that already have unique keys, that therefore do +# not need for Slony-I to add sequences/indices + at KEYEDTABLES=( + "balance_history", + "billing_account", + "bl_update_reason", + "epp_activity", + "epp_contact", + "epp_contact_map", + "epp_contact_status", + "epp_dns_update", + "epp_domain", + "epp_domain_contact", + "epp_domain_host", + "epp_domain_protocol", + "epp_domain_protocol_history", + "epp_domain_status", + "epp_domain_trn_contact", + "epp_domain_trn_registrant", + "epp_host", + "epp_host_ip", + "epp_host_status", + "epp_poll_queue", + "epp_registrar", + "epp_registrar_contact", + "epp_registrar_notification", + "epp_registrar_role", + "epp_registrar_status", + "epp_registrar_tld", + "epp_registrar_zone", + "epp_role", + "epp_server", + "epp_trans_log", + "epp_trans_reason", + "epp_user", + "fee_schedule", + "flex_tld", + "flex_zone", + "idn_script" + ); + +# Here are the tables to be replicated that do NOT have unique +# keys, to which Slony-I will have to add a key field + at SERIALTABLES=( + "epp_registrar_ipallow", + "epp_domain_renew", + "billing_event_logger", + "epp_registrar_low_threshold", + "epp_domain_archive", + "bl_update_history", + "res_country", + "billing_price", + "epp_log_1", + "epp_log_2", + "epp_log_3", + "epp_log_4", + "epp_log_5", + "epp_log_6", + "epp_log_7", + "epp_log_8", + "epp_log_9", + "billing_transaction_posted", + "billing_balance_history" + ); + +# These are the applications' sequences that are to be +# replicated + at SEQUENCES=( + "reserved_names_seq", + "epp_log_seq_", + "whois_cachemgmt_seq", + "flex_tld_id_seq", + "domain_seq", + "billing_seq", + "domain_lock_id_seq", + "bl_update_reason_id_seq", + "epp_log_active_seq", + "domain_id_seq", + "registrar_notification_id_seq", + "poll_id_seq", + "host_id_seq", + "fee_schedule_seq", + "registrar_id_seq", + "contact_id_seq", + "afilias_billable_trns_seq", + "trid_seq", + "role_id_seq", + "rpt_registrar_stats_id_seq", + "whois_activity_seq", + "epp_trans_log_id_seq", + "epp_activity_seq", + "bl_update_history_id_seq", + "whois_cachemgmt_server_seq", + "rrp_trid_seq", + "user_id_seq", + "dns_update_id_seq" + ); + --- /dev/null +++ tools/altperl/drop_set.pl @@ -0,0 +1,28 @@ +#!/usr/bin/perl +# $Id: drop_set.pl,v 1.1 2004/07/25 04:02:50 cbbrowne Exp $ +# Author: Christopher Browne +# Copyright 2004 Afilias Canada + +require 'slon-tools.pm'; +require 'slon.env'; +my ($set) = @ARGV; +if ($set =~ /^set(\d+)$/) { + $set = $1; +} else { + print "Need set identifier\n"; + croak "drop_set.pl setN\n"; +} + +open(SLONIK, "|slonik"); + +print SLONIK genheader(); + +print SLONIK qq{ + try { + drop set (id = $set, origin=1); + } + on error { + exit 1; + } + echo 'Dropped set $set'; +}; --- /dev/null +++ tools/altperl/slon_start.pl @@ -0,0 +1,55 @@ +#!/usr/bin/perl +# $Id: slon_start.pl,v 1.1 2004/07/25 04:02:50 cbbrowne Exp $ +# Author: Christopher Browne +# Copyright 2004 Afilias Canada + +#start the slon daemon +require 'slon-tools.pm'; +require 'slon.env'; + +$node =$ARGV[0]; + +if ( scalar(@ARGV) < 1 ) { + die "Usage: ./slon_start [node]\n"; +} + +if ($node =~ /^node\d+$/) { + # Node name is in proper form +} else { + print "Valid node names are node1, node2, ...\n\n"; + die "Usage: ./slon_start [node]\n"; +} + +get_pid(); + +if ($pid) { + die "Slon is already running for set $SETNAME!\n"; +} + +$node =~ /node(\d*)$/; +$nodenum = $1; +my $dsn = $DSN[$nodenum]; +my $dbname=$DBNAME[$nodenum]; +system "$SLON_BIN_PATH/slon $SETNAME -s 1000 -d2 '$dsn' 2>$LOGDIR/slon-$dbname-$node.err >$LOGDIR/slon-$dbname-$node.out &"; + +get_pid(); + +if (!($pid)){ + print "Slon failed to start for set $SETNAME!\n"; +} else { + print "Slon successfully started for set $SETNAME\n"; + print "PID [$pid]\n"; +} +#start the watchdog process +system " perl slon_watchdog.pl $node 30 &"; + +sub get_pid { + $node =~ /node(\d*)$/; + my $nodenum = $1; + my ($dbname, $dbport) = ($DBNAME[$nodenum], $PORT[$nodenum]); +# print "Searching for PID for $dbname on port $dbport\n"; + open(PSOUT, "ps -auxww | egrep \"[s]lon $SETNAME\" | egrep \"dbname=$dbname .*port=$dbport\" | sort -n | awk '{print \$2}'|"); + $pid = <PSOUT>; + chop $pid; + close(PSOUT); +} --- /dev/null +++ tools/altperl/reset_cluster.pl @@ -0,0 +1,50 @@ +#!/usr/bin/perl +# $Id: reset_cluster.pl,v 1.1 2004/07/25 04:02:50 cbbrowne Exp $ +# Author: Christopher Browne +# Copyright 2004 Afilias Canada + +require 'slon-tools.pm'; +require 'slon.env'; + +open(SLONIK, ">/tmp/slonik.$$"); + +print SLONIK genheader(); + +my ($dbname, $dbhost)=($DBNAME[1], $HOST[1]); +print SLONIK " + try { +"; + +foreach my $node (@NODES) { + if ($node > 1) { + my ($dbname, $dbhost) = ($DBNAME[$node], $HOST[$node]); + print SLONIK " store node (id = $node, comment = 'Node $dbname@$dbhost');\n"; + } +} + +foreach my $nodea (@NODES) { + my $dsna = $DSN[$nodea]; + foreach my $nodeb (@NODES) { + if ($nodea != $nodeb) { + my $dsnb = $DSN[$nodeb]; + print SLONIK " store path (server = $nodea, client = $nodeb, conninfo = '$dsna');\n"; + print SLONIK " store path (server = $nodeb, client = $nodea, conninfo = '$dsnb');\n"; + print SLONIK " store listen (origin = $nodea, receiver = $nodeb);\n"; + print SLONIK " store listen (origin = $nodeb, receiver = $nodea);\n"; + } + } +} +} +print SLONIK qq[ + } + on error { + exit 1; + } + echo 'Replication nodes prepared'; + echo 'Please start the replication daemon on both systems'; + +]; + +close SLONIK; +`slonik < /tmp/slonik.$$`; +unlink("/tmp/slonik.$$"); --- /dev/null +++ tools/altperl/unsubscribe_set.pl @@ -0,0 +1,38 @@ +#!/usr/bin/perl +# $Id: unsubscribe_set.pl,v 1.1 2004/07/25 04:02:51 cbbrowne Exp $ +# Author: Christopher Browne +# Copyright 2004 Afilias Canada + +require 'slon-tools.pm'; +require 'slon.env'; + +my ($set, $node) = @ARGV; +if ($node =~ /^node(\d+)$/) { + $node = $1; +} else { + print "Need to specify node!\n"; + die "unsubscribe_set setM nodeN\n"; +} + +if ($set =~ /^set(\d+)$/) { + $set = $1; +} else { + print "Need to specify set!\n"; + die "unsubscribe_set setM nodeN\n"; +} + +open(SLONIK, ">/tmp/slonik-subscribe.$$"); +print SLONIK genheader(); +print SLONIK qq{ + try { + unsubscribe set (id = $set, receiver = $node); + } + on error { + echo 'Failed to unsubscribe node $node from set $set'; + exit 1; + } + echo 'unsubscribed node $node from set $set'; +}; +close SLONIK; +print `slonik < /tmp/slonik-unsubscribe.$$`; +unlink("/tmp/slonik-unsubscribe.$$"); --- /dev/null +++ tools/altperl/restart_node.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +# $Id: restart_node.pl,v 1.1 2004/07/25 04:02:50 cbbrowne Exp $ +# Author: Christopher Browne +# Copyright 2004 Afilias Canada + +require 'slon-tools.pm'; +require 'slon.env'; + +foreach my $node (@NODES) { + my $dsn = $DSN[$node]; + open(SLONIK, "|slonik"); + print SLONIK qq{ + cluster name = $SETNAME ; + node $node admin conninfo = '$dsn'; + restart node $node; + }; + close SLONIK; +} --- /dev/null +++ tools/altperl/failover.pl @@ -0,0 +1,39 @@ +#!/usr/bin/perl +# $Id: failover.pl,v 1.1 2004/07/25 04:02:50 cbbrowne Exp $ +# Author: Christopher Browne +# Copyright 2004 Afilias Canada + +require 'slon-tools.pm'; +require 'slon.env'; + +my ($node1, $node2) = @ARGV; +if ($node1 =~ /^node(\d+)$/) { + $node1 = $1; +} else { + print "Valid set names are set1, set2, ...\n\n"; + die "Usage: ./failover.pl nodeN setOLD setNEW\n"; +} +if ($node2 =~ /^node(\d+)$/) { + $node2 = $1; +} else { + print "Valid set names are set1, set2, ...\n\n"; + die "Usage: ./failover.pl nodeN setOLD setNEW\n"; +} + +open(SLONIK, ">/tmp/slonik.$$"); +print SLONIK genheader(); +my ($dbname, $dbhost)=($DBNAME[1], $HOST[1]); +print SLONIK qq[ + try { + failover (id = $node1, backup node = $node2); + } + on error { + echo 'Failure to fail node $node1 over to $node2'; + exit 1; + } + echo 'Replication sets originating on $node1 failed over to $node2'; +]; + +close SLONIK; +`slonik < /tmp/slonik.$$`; +unlink("/tmp/slonik.$$"); --- /dev/null +++ tools/altperl/drop_node.pl @@ -0,0 +1,31 @@ +#!/usr/bin/perl +# $Id: drop_node.pl,v 1.1 2004/07/25 04:02:50 cbbrowne Exp $ +# Author: Christopher Browne +# Copyright 2004 Afilias Canada + +require 'slon-tools.pm'; +require 'slon.env'; + +my ($node) = @ARGV; +if ($node =~ /^node(\d+)$/) { + $node = $1; +} else { + print "Need to specify node!\n"; + die "drop_node nodeN\n"; +} + +open(SLONIK, ">/tmp/slonik-drop.$$"); +print SLONIK genheader(); +print SLONIK qq{ + try { + drop node (id = $node); + } + on error { + echo 'Failed to drop node $node from cluster'; + exit 1; + } + echo 'dropped node $node cluster'; +}; +close SLONIK; +print `slonik < /tmp/slonik-drop.$$`; +unlink("/tmp/slonik-drop.$$"); --- /dev/null +++ tools/altperl/slon-tools.pm @@ -0,0 +1,73 @@ +#!/usr/bin/perl +# $Id: slon-tools.pm,v 1.1 2004/07/25 04:02:50 cbbrowne Exp $ +# Author: Christopher Browne +# Copyright 2004 Afilias Canada + +sub add_node { + my %PARAMS = (host=> undef, + dbname => 'template1', + port => 5432, + user => 'postgres', + node => undef, + password => undef, + parent => 1, + noforward => undef + ); + my $K; + while ($K= shift) { + $PARAMS{$K} = shift; + } + die ("I need a node number") unless $PARAMS{'node'}; + my $node = $PARAMS{'node'}; + push @NODES, $node; + my $loginstr; + my $host = $PARAMS{'host'}; + if ($host) { + $loginstr .= "host=$host"; + $HOST[$node] = $host; + } else { + die("I need a host name") unless $PARAMS{'host'}; + } + my $dbname = $PARAMS{'dbname'}; + if ($dbname) { + $loginstr .= " dbname=$dbname"; + $DBNAME[$node] = $dbname; + } + my $user=$PARAMS{'user'}; + $loginstr .= " user=$user"; + $USER[$node]= $user; + + my $port = $PARAMS{'port'}; + if ($port) { + $loginstr .= " port=$port"; + $PORT[$node] = $port; + } + my $password = $PARAMS{'password'}; + if ($password) { + $loginstr .= " password=$password"; + $PASSWORD[$node] = $password; + } + $DSN[$node] = $loginstr; + my $parent = $PARAMS{'parent'}; + if ($parent) { + $PARENT[$node] = $parent; + } + my $noforward = $PARAMS{'noforward'}; + if ($noforward) { + $NOFORWARD[$node] = $noforward; + } +} + +# This is the usual header to a slonik invocation that declares the +# cluster name and the set of nodes and how to connect to them. +sub genheader { + my $header = "cluster name = $SETNAME;\n"; + foreach my $node (@NODES) { + if ($DSN[$node]) { + my $dsn = $DSN[$node]; + $header .= " node $node admin conninfo='$dsn';\n"; + } + } + return $header +} +return 1; --- /dev/null +++ tools/altperl/ToDo @@ -0,0 +1,20 @@ +- Need to write a "repair_cluster.pl" script that + modifies configuration to reflect new configuration, + dropping and adding paths and listeners. + + This would compare the configuration computed (as in init_cluster) + with the configuration actually found on some node, add "additional" + bits, and drop obsolete bits. + +- It would seem likely that the function "generate_listen_paths()" in + init_cluster.pl would be beneficial to port to pl/pgsql, as + there presently isn't any capability to rebuild the listener + paths by automatically dropping the old ones. + +- At present, the configuration generated by this set of tools is + fairly fragile. If just about any sort of error is made, it is + commonly needful to drop all of the Slony schemas, thereby cleaning + _everything_ out, and restarting the configuration process from + scratch. + + That certainly isn't ideal. --- /dev/null +++ tools/altperl/update_node.pl @@ -0,0 +1,17 @@ +#!/usr/bin/perl +# $Id: update_node.pl,v 1.1 2004/07/25 04:02:51 cbbrowne Exp $ +# Author: Christopher Browne +# Copyright 2004 Afilias Canada + +require 'slon-tools.pm'; +require 'slon.env'; + +open(SLONIK, "|slonik"); +print SLONIK qq{ + cluster name = $SETNAME ; + node 1 admin conninfo = '$CINFO1'; + node 2 admin conninfo = '$CINFO2'; + + update functions (id = 1); + update functions (id = 2); +}; --- /dev/null +++ tools/altperl/uninstall_node.pl @@ -0,0 +1,26 @@ +#!/usr/bin/perl +# $Id: uninstall_node.pl,v 1.1 2004/07/25 04:02:51 cbbrowne Exp $ +# Author: Christopher Browne +# Copyright 2004 Afilias Canada + +require 'slon-tools.pm'; +require 'slon.env'; +#use Pg; +open(SLONIK, "|slonik"); +print SLONIK genheader(); +print SLONIK qq{ + uninstall node (id=1); +}; +close SLONIK; + +foreach my $node (@NODES) { + foreach my $command ("drop schema _$SETNAME cascade;") { + print $command, "\n"; + print `echo "$command" | psql -h $HOST[$node] -U $USER[$node] -d $DBNAME[$node] -p $PORT[$node]`; + } + foreach my $t (@SERIALTABLES) { + my $command = "alter table $t drop column \\\"_Slony-I_" . $SETNAME . "_rowID\\\";"; + print $command, "\n"; + print `echo "$command" | psql -h $HOST[$node] -U $USER[$node] -d $DBNAME[$node] -p $PORT[$node]`; + } +} --- /dev/null +++ tools/altperl/slon_watchdog.pl @@ -0,0 +1,60 @@ +#!/usr/bin/perl +# $Id: slon_watchdog.pl,v 1.1 2004/07/25 04:02:51 cbbrowne Exp $ +# Author: Christopher Browne +# Copyright 2004 Afilias Canada + +require 'slon-tools.pm'; +require 'slon.env'; + +$node =$ARGV[0]; +$sleep =$ARGV[1]; + +if ( scalar(@ARGV) < 2 ) { + die "Usage: ./slon_watchdog node sleep-time\n"; +} + +slon_watchdog(); + +sub slon_watchdog { + get_pid(); + if (!($pid)) { + if ($node eq "node1") { + open (SLONLOG, ">>$LOGDIR/slon-$DBNAME1.out"); + print SLONLOG "WATCHDOG: No Slon is running for set $SETNAME!\n"; + print SLONLOG "WATCHDOG: You ought to check the postmaster and slon for evidence of a crash!\n"; + print SLONLOG "WATCHDOG: I'm going to restart slon for $node...\n"; + #first restart the node + system "./restart_node.sh"; + system "$SLON_BIN_PATH/slon $SETNAME -s 1000 -d2 'dbname=$DBNAME1 port=$DBPORT1' 2>$LOGDIR/slon-$DBNAME1.err >$LOGDIR/slon-$DBNAME1.out &"; + get_pid(); + print SLONLOG "WATCHDOG: Restarted slon for set $SETNAME, PID $pid\n"; + } elsif ($node eq "node2") { + open (SLONLOG, ">>$LOGDIR/slon-$DBNAME2.out"); + print SLONLOG "WATCHDOG: No Slon is running for set $SETNAME!\n"; + print SLONLOG "WATCHDOG: You ought to check the postmaster and slon for evidence of a crash!\n"; + print SLONLOG "WATCHDOG: I'm going to restart slon for $node...\n"; + #first restart the node + system "./restart_node.sh"; + system "$SLON_BIN_PATH/slon $SETNAME -s 1000 -d2 'dbname=$DBNAME2 port=$DBPORT2' 2>$LOGDIR/slon-$DBNAME2.err >$LOGDIR/slon-$DBNAME2.out &"; + get_pid(); + print SLONLOG "Restarted slon for set $SETNAME, PID $pid\n"; + } + } else { + open(LOG, ">>$LOGDIR/slon_watchdog.log"); + print LOG "\n"; + system "date >> $LOGDIR/slon_watchdog.log"; + print LOG "Found slon daemon running for set $SETNAME, PID $pid\n"; + print LOG "Looks Ok\n"; + print LOG "Sleeping for $sleep seconds\n"; + } + close(PSOUT); + sleep $sleep; + slon_watchdog(); +} + +sub get_pid { + open(PSOUT, "ps -auxww | grep -v grep | grep \"slon $SETNAME\" | sort -n | awk '{print \$2}'|"); + $pid = <PSOUT>; + chop $pid; + close(PSOUT); +} --- /dev/null +++ tools/altperl/move_set.pl @@ -0,0 +1,52 @@ +#!/usr/bin/perl +# $Id: move_set.pl,v 1.1 2004/07/25 04:02:50 cbbrowne Exp $ +# Author: Christopher Browne +# Copyright 2004 Afilias Canada + +require 'slon-tools.pm'; +require 'slon.env'; + +my ($set, $node1, $node2) = @ARGV; +if ($set =~ /^set(\d+)$/) { + # Node name is in proper form + $set = $1; +} else { + print "Valid set names are set1, set2, ...\n\n"; + die "Usage: ./move_set.pl setN nodeOLD nodeNEW\n"; +} + +if ($node1 =~ /^node(\d+)$/) { + $node1 = $1; +} else { + print "Valid node names are node1, node2, ...\n\n"; + die "Usage: ./move_set.pl setN nodeOLD nodeNEW\n"; +} +if ($node2 =~ /^node(\d+)$/) { + $node2 = $1; +} else { + print "Valid node names are node1, node2, ...\n\n"; + die "Usage: ./move_set.pl setN nodeOLD nodeNEW\n"; +} + +open(SLONIK, ">/tmp/slonik.$$"); +print SLONIK genheader(); +my ($dbname, $dbhost)=($DBNAME[1], $HOST[1]); +print SLONIK qq[ + try { + echo 'Locking down set $set on node $node1'; + lock set (id = $set, origin = $node1); + echo 'Locked down - moving it'; + move set (id = $set, old origin = $node1, new origin = $node2); + unlock set (id = $set, origin = $node2); + } + on error { + echo 'Failure to move set $set from $node1 to $node2'; + unlock set (id = $set, origin = $node1); + exit 1; + } + echo 'Replication set $set moved from node $node1 to $node2'; +]; + +close SLONIK; +`slonik < /tmp/slonik.$$`; +unlink("/tmp/slonik.$$"); --- /dev/null +++ tools/altperl/slon_pushsql.pl @@ -0,0 +1,31 @@ +#!/usr/bin/perl +# $Id: slon_pushsql.pl,v 1.1 2004/07/25 04:02:50 cbbrowne Exp $ +# Author: Christopher Browne +# Copyright 2004 Afilias Canada + +require 'slon-tools.pm'; +require 'slon.env'; +my ($set, $node, $file) = @ARGV; +if ($set =~ /^set(\d+)$/) { + $set = $1; +} else { + print "Invalid set identifier"; + die "Usage: ./slon_pushsql.pl set[N] node[N] sql_script_file\n"; +} +if ($node =~ /^node(\d+)$/) { + $node = $1; +} else { + print "Invalid node identifier"; + die "Usage: ./slon_pushsql.pl set[N] node[N] sql_script_file\n"; +} + +open(SLONIK, "|slonik"); +print SLONIK genheader(); + +print SLONIK qq{ + execute script ( + set id=$set, + filename='$file', + event node = $node + ); +}; --- /dev/null +++ tools/altperl/slon_kill.pl @@ -0,0 +1,47 @@ +#!/usr/bin/perl +# $Id: slon_kill.pl,v 1.1 2004/07/25 04:02:50 cbbrowne Exp $ +# Kill all slon instances for the current setname +# Author: Christopher Browne +# Copyright 2004 Afilias Canada + +require 'slon-tools.pm'; +require 'slon.env'; + +print "slon_kill.pl... Killing all slon and slon_watchdog instances for setname $SETNAME\n"; +print "1. Kill slon watchdogs\n"; +#kill the watchdog + +open(PSOUT, "ps auxww | egrep '[s]lon_watchdog' | sort -n | awk '{print \$2}'|"); +$found="n"; +while ($pid = <PSOUT>) { + chomp $pid; + if (!($pid)){ + print "No slon_watchdog is running for set $SETNAME!\n"; + } else { + $found="y"; + system "kill $pid"; + print "slon_watchdog for set $SETNAME killed - PID [$pid]\n"; + } +} +close(PSOUT); +if ($found eq 'n') { + print "No watchdogs found\n"; +} +print "\n2. Kill slon processes\n"; +#kill the slon daemon +$found="n"; +open(PSOUT, "ps auxww | egrep \"[s]lon .*$SETNAME\" | sort -n | awk '{print \$2}'|"); +while ($pid = <PSOUT>) { + chomp $pid; + if (!($pid)) { + print "No Slon is running for set $SETNAME!\n"; + } else { + system "kill -9 $pid"; + print "Slon for set $SETNAME killed - PID [$pid]\n"; + $found="y"; + } +} +close(PSOUT); +if ($found eq 'n') { + print "No slon processes found\n"; +} --- /dev/null +++ tools/altperl/README @@ -0,0 +1,64 @@ +README +$Id: README,v 1.1 2004/07/25 04:02:50 cbbrowne Exp $ + +Christopher Browne +Database Administrator +Afilias Canada + +This is a "second system" set of scripts for managing a set of Slony-I +instances. + +Unlike the shell scripts that have been used, previously, it allows +having an arbitrary number of Slony-I nodes. They are configured in +slon.env by calling add_node() once for each node that is needed. + +slon.env also contains lists of tables that are to be replicated: + + @KEYEDTABLES contains all of the tables that have unique keys + + @SERIALTABLES contains tables that do not have a unique key + to which Slony-I will need to add and populate + a unique key + + @SEQUENCES lists all of the application sequences that are to be + replicated. + +Alas, this means that the values are "hardcoded" as far as the tools +are concerned. + +The natural extension to be added to this to make it more flexible +would be for slon.env to look at an environment variable to see what +file these lists are found in. That way, you could do something like: + + for i in `seq 10`; do + SLONYENV="./set$i.config" ./init_cluster.pl + done + +Steps to start up replication + +0. Dump from source system to destination + pg_dump -s -c flex1 | psql flex2 + +1. Initializes the Slony cluster + ./init_cluster.pl + + This sets up a FULL cross-join set of paths and listeners, doing + something of a shortest-path evaluation of which "store listens" to + set up. + +2. Start up slon servers for both DB instances + ./slon_start.pl node1 + ./slon_start.pl node2 + +3. Sets up all the tables for "set 1" for FlexReg 2.0 + ./create_set.pl set1 + +4. Subscribe Node #2 to Set #1 + ./subscribe_set.pl set1 node2 + This is the Big One... + +That SHOULD be it, although "should" is probably too strong a word :-) + +There are numerous other tools for adding/dropping Slony-I +configuration, and scripts that might manage simple forms of +switchover/failover.
- Previous message: [Slony1-commit] By cbbrowne: New Directory
- Next message: [Slony1-commit] By darcyb: Work around braindead m4 processors on some OS's also to be
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
More information about the Slony1-commit mailing list