#! /usr/bin/perl # # Author: Kevin C. Miller # http://www.andrew.cmu.edu/~kevinm/dhcp/failover.html # # The copyright is with the author. We (SuSE) include this script # with his permission. # # NOTE: you have to edit this script to make it functional for you. ## Reset IPs that are stuck in some weird state on the DHCP servers ## You can run it standalone, and it just reports statistics ## ## Or run with option '-reset' to reset many of the problem IPs ## Or run with option '-primary' to only reset problem IPs on the primary ## ## ** Note: You probably want to run -primary if you are in a low-free-IP ## situation. In this case -reset may enter a race condition and ## reset valid IPs on the secondary. use strict; my $PRIMARY = "/afs/andrew/usr/kevinm/dhcp1"; my $PRIMARY_IP = '128.2.4.2'; my $SEC = "/afs/andrew/usr/kevinm/dhcp3"; my $SEC_IP = '128.2.32.38'; my $RESET = "/afs/andrew/usr/kevinm/bin/clearip"; my %curstate; my %nextstate; my ($TotalPrimary, $TotalSecondary) = (0,0); my ($LeasesDiff, $LeasesEq) = (0,0); open(FILE, $PRIMARY) || die "Cannot open primary $PRIMARY"; while() { next if ($_ =~ /^\#/); my @a = split(/\s+/, $_); $curstate{$a[0]} = $a[1]; $nextstate{$a[0]} = $a[3]; $TotalPrimary++; } close(FILE); my @ResetPrimary; my @ResetSecondary; open(FILE, $SEC) || die "Cannot open secondary $SEC"; while() { next if ($_ =~ /^\#/); my @a = split(/\s+/, $_); my ($key, $cur, $next) = ($a[0], $a[1], $a[3]); $TotalSecondary++; if ($curstate{$key} ne $cur && ($nextstate{$key} ne $next || $nextstate{$key} eq '' && $next eq '')) { ## There is some difference $LeasesDiff++; if ($curstate{$key} eq 'active' && $nextstate{$key} eq 'expired' && $cur eq 'expired' && $next eq 'free') { # Reset both push(@ResetPrimary, $key); push(@ResetSecondary, $key); }elsif($curstate{$key} eq 'expired' && $nextstate{$key} eq 'free' && $cur eq 'active' && $next eq 'expired') { # Reset both push(@ResetPrimary, $key); push(@ResetSecondary, $key); }elsif( ($curstate{$key} eq 'backup' && $cur eq 'free') ) { # Reset both push(@ResetPrimary, $key); push(@ResetSecondary, $key); }elsif($curstate{$key} eq 'expired' && $nextstate{$key} eq 'free' && $cur eq 'free') { # Reset both push(@ResetPrimary, $key); push(@ResetSecondary, $key); }elsif(!defined $curstate{$key} && !defined $nextstate{$key}) { push(@ResetSecondary, $key); } # There are other tests that could be made (primary # thinks the lease is 'backup', secondary doesn't know # about it).. etc. }else{ # No difference in state $LeasesEq++; } delete $curstate{$key}; delete $nextstate{$key}; } # Go through all the IPs on the primary that aren't defined # on the secondary at all. foreach my $key (keys %curstate) { if ($curstate{$key} eq 'backup') { push(@ResetPrimary, $key); } } if ($ARGV[0] eq '-reset') { reset_ips($PRIMARY_IP, $SEC_IP, \@ResetPrimary, \@ResetSecondary, $RESET); }elsif($ARGV[0] eq '-primary') { reset_ips($PRIMARY_IP, '', \@ResetPrimary, [], $RESET); } # Print statistics print "Leases compared. Primary: $TotalPrimary Secondary: $TotalSecondary\n"; print " Different: $LeasesDiff Equal: $LeasesEq\n"; print " (Different+Equal may not add up to Primary, due to \n"; print " missing entries in the leases file.)\n"; exit(1); ## *********************************************************************** # Arguments: # - IP Address of primary DHCP server # - IP Address of secondary DHCP server # - Reference to an array of IPs to reset on the primary # - Reference to an array of IPs to reset on the secondary # - Reset script sub reset_ips { my ($PrimaryIP, $SecIP, $rResetPrimary, $rResetSec, $Prog) = @_; foreach my $IP (@$rResetPrimary) { print "Resetting $IP on $PrimaryIP\n"; `$Prog $IP $PrimaryIP`; } foreach my $IP (@$rResetSec) { print "Resetting $IP on $SecIP\n"; `$Prog $IP $SecIP`; } }