diff --git a/README.md b/README.md index d3bef19..b551b21 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,23 @@ # cluster_cron A perlscript that makes it possible to cluster cron via a shared drive + +In order to use this you need a shared drive on all servers that is going to be part of the cluster such as afs, glusterfs, nfs or samba. + +Install this script somwhere and make sure it is executable. In this example the script will be put in: +/usr/local/bin/cluster_cron.pl + +Next you need to add the script to the crontab of some user which will not be synced on both servers, for example the root user. + +The script takes 4 arguments of which two are optional: +user = the user that gets the cron file synced, in this example we are using a user called cluster +shared directory = the shared drive you set up previously, in this example the path is /mnt/shareddir +mode = either 0 for active/passive or 1 for active/active if this argument is not supplied 1 is assumed +cron spool directory = defaults to /var/spool/cron/crontabs + +So as root: +crontab -e + +add this line: +``` +* * * * * /usr/local/bin/cluster_cron.pl cluster /mnt/shareddir 0 /var/spool/cron/crontabs >> /mnt/shareddir/cluster.log 2>&1 +``` diff --git a/cluster_cron2.pl b/cluster_cron2.pl deleted file mode 100755 index b720670..0000000 --- a/cluster_cron2.pl +++ /dev/null @@ -1,188 +0,0 @@ -#!/usr/bin/env perl -use warnings; -use strict; -use bignum; -use Sys::Hostname; -use File::Compare; -use File::Copy; -use File::Temp; - -my $num_args = $#ARGV + 1; -if ($num_args < 2) { - print "\nUsage: $0 [cron spool directory]\n"; - exit; -} - -# We need a common directory for our nodes to write and read state from plus a couple of cronfiles -my ($user, $shareddir, $spooldir) = @ARGV; - -# Set some defaults if we didn't get them -unless ($spooldir) { - $spooldir = "/var/spool/cron/crontabs"; -} - -my $sharedcrondir = "$shareddir/crontab"; -my $cronfile = "$spooldir/$user"; -my $activesharedcronfile = "$sharedcrondir/$user"; -my $oldactivesharedcronfile = "$sharedcrondir/$user.old"; -my $electiondir = "$sharedcrondir/election"; -my $hostname = hostname; -my $failovertimeout = 40; - - -# Create directories -unless (-d $sharedcrondir) { - mkdir $sharedcrondir or die "Can not create shared directory: $!"; -} - - - -# Get the nodes in the cluster -sub get_nodes { - # Our data type is a hash with the following fields - # name, time, numeric - my @nodes = (); - my @files = <$electiondir/*>; - foreach my $filename (@files) { - chomp $filename; - my $nodename = $filename; - $nodename =~ s/^$electiondir\///; - # Don't add this host - unless ($nodename eq $hostname) { - my $node = {}; - open(FILE, "<$filename") or die "Could not open file: $!"; - $node->{'name'} = $nodename; - my $content = ; - chomp $content; - $node->{'time'} = $content; - $node->{'numeric'} = get_numeric($nodename); - close FILE; - push @nodes, $node; - } - } - return @nodes; - - -} - - -# Simply write a unix timestamp to a file with our name on it -sub write_timestamp { - my $time = time; - open(FILE,"+>$electiondir/$hostname"); - print FILE $time; - close FILE; - -} - -# Lets see if we are the active node -sub is_active { - - my @nodes = @_; - - # Assume you are active - my $state = 1; - # Loop all nodes - foreach my $node (@nodes) { - # If it has been active in the last 120 seconds it is eligable - my $diff = time - $node->{'time'}; - if( $diff < $failovertimeout ) { - print "Node: $node->{'name'} was active last $failovertimeout sec\n"; - # Go to passive mode if the numeric of that host is less than that of this host - my $this = get_numeric($hostname); - my $that = $node->{'numeric'}; - print "Numeric for $hostname is $this and for $node->{'name'} is $that\n"; - if( $this > $that ) { - $state = 0 ; - print "I am not active\n"; - } else { - print "I am active\n"; - my $outfile = "/var/lib/thruk/crontab/active"; - open(OUTFILE,"+>$outfile"); - print OUTFILE $hostname; - close OUTFILE; - } - } else { - print "Node: $node->{'name'} was not active last 120 sec\n"; - - } - } - return $state; -} - -# Turn a hostname in to a decimal number used for comparison, lowest numer gets to be active if it is working -sub get_numeric { - my ($host) = @_; - my @ascii = unpack 'C*', $host; - my $numeric = ''; - foreach my $val (@ascii) { - $numeric .= $val; - } - return $numeric + 0; -} - -sub cron_compare { - my ($one, $two, $three) = @_; - - # Current cronfile and shared cronfile is not same - if(compare($one, $two) != 0) { - - print "$one and $two is not the same\n"; - - # This means that the other node has changed the cron file - if(compare($one, $three) == 0) { - - print "Some other node has changed the file\n"; - return 1; - # This means that my node has changed the cron file - } else { - - print "This node has changed the cron file\n"; - return 2; - } - } - - return 0; - -} - -sub run { - print "Host: $hostname started run at: " . time . "\n"; - print "User: $user, shareddir: $shareddir and spooldir: $spooldir\n"; - - if(-e $activesharedcronfile) { - unless(-e $oldactivesharedcronfile) { - copy($activesharedcronfile, $oldactivesharedcronfile); - } - } - - unless (-d $electiondir) { - mkdir $electiondir; - } - # Update timestamp - write_timestamp; - is_active(get_nodes); - my $compare = cron_compare($cronfile, $activesharedcronfile, $oldactivesharedcronfile ); - # The other node has changed the file - if($compare == 1) { - copy($activesharedcronfile, $cronfile); - copy($activesharedcronfile, $oldactivesharedcronfile); - # We have changed things - } elsif ($compare == 2) { - copy($cronfile, $activesharedcronfile); - } - - print "Host: $hostname finished run at: " . time . "\n\n\n"; -} - -for (my $i = 0; $i < 6; $i++) { - my $start = time; - $| = 1; - run; - if ((my $remaining = 10 - (time - $start)) > 0) { - sleep $remaining; - } -} - -exit; -