#!/usr/local/bin/perl use Net::DHCP::Control::Failover 'failover_statename'; use MIME::Base64; use Getopt::Std; use Carp; my %auth = (key_name => 'omkey', key_type => 'hmac-md5', key => MIME::Base64::decode_base64("K/E3ho1EbqGk1mYPNpiIcmpuR/rFG6T689HHGI78ekM="), ); my %host = ( # primary => 'flotsam.net.isc.upenn.edu', primary => 'noc-2003-ist.NET.ISC.UPENN.EDU', # secondary => 'jetsam.net.isc.upenn.edu', secondary => 'noc-2003-dmr.net.isc.upenn.edu', ); getopts('a:t:n:') or usage(); $opt_a or usage(); if ($opt_a eq 'status') { show_status(); } elsif ($opt_a eq 'change_state') { usage() unless defined $opt_t && defined $opt_n; usage() unless exists $host{$opt_t}; usage() unless defined failover_statename($opt_n); change_state(auth => \%auth); } elsif ($opt_a eq 'ipstats') { die "not implemented\n"; } else { usage(); } sub show_status { for my $what (keys %host) { printf "%9s: %12s\n", $what, get_state(host => $host{$what}, auth => \%auth, ); } } sub get_state { my (%opts) = @_; my $fo = $opts{handle} || Net::DHCP::Control::Failover::State->new(host => $opts{host}, %{$opts{auth}}, attrs => { name => "dhcp.net.isc.upenn.edu" }, ) || return failover_statename(0); my $state = $fo->get("local-state"); defined($state) or die "Couldn't get failover local state: $Net::DHCP::Control::STATUS"; failover_statename($state); } my %legal_transition; BEGIN { $legal_transition{recover} = {target => 'shutdown', peer => 'partner down', }; $legal_transition{shutdown} = {target => 'normal', peer => 'normal', }; $legal_transition{'partner down'} = {target => 'communications interrupted', peer => 'unavailable', }; } sub change_state { my %opts = @_; my (%state, %handle); my $target = $opt_t; my $new_state = $opt_n; my %map; $map{target} = $target; $map{peer} = $target eq 'primary' ? 'secondary' : 'primary'; print "changing state\n"; for my $what (qw(target peer)) { $handle{$what} = Net::DHCP::Control::Failover::State->new(host => $host{$map{$what}}, %{$opts{auth}}, attrs => { name => "dhcp.net.isc.upenn.edu" }, ); $state{$what} = get_state(handle => $handle{$what}); printf "%9s: %12s\n", $what, $state{$what}; } if ($state{target} eq $new_state) { warn "target is already in state '$new_state' no change made\n"; exit 0; } my $required_current_state = $legal_transition{$new_state} or die "invalid new state\n"; for my $what (qw(target peer)) { unless ($state{$what} eq $required_current_state->{$what}) { die "To transit the target to state $new_state, the $what must be in state $required_current_state->{$what}. But it is in state $state{$what} instead.\n"; } } set_state(handle => $handle{target}, state => failover_statename($new_state)); } sub set_state { my %opts = @_; my $fo = $opts{handle} || Net::DHCP::Control::Failover::State->new(host => $opts{host}, %{$opts{auth}}, attrs => { name => "dhcp.net.isc.upenn.edu" }, ) || die "Couldn't create failover-state object: $Net::DHCP::Control::STATUS"; $fo->set("local-state", $opts{state}) or die "Couldn't set local state: $Net::DHCP::Control::STATUS"; } sub usage { warn <