CVS User Account cvsuser
Sun Jul 25 04:03:01 PDT 2004
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.


More information about the Slony1-commit mailing list