diff options
author | Stig Thormodsrud <stig@vyatta.com> | 2008-06-06 13:47:36 -0700 |
---|---|---|
committer | Stig Thormodsrud <stig@vyatta.com> | 2008-06-06 13:47:36 -0700 |
commit | f498c7b59b8a4784c14b3affcb2d796ab3814138 (patch) | |
tree | 4a870cc9ac25ed5a1b573d27616f52a8ba996e3a /scripts/keepalived | |
parent | fff0ed76b20569d50f8e2aca1500602ab6686b21 (diff) | |
download | vyatta-cfg-quagga-f498c7b59b8a4784c14b3affcb2d796ab3814138.tar.gz vyatta-cfg-quagga-f498c7b59b8a4784c14b3affcb2d796ab3814138.zip |
Fix 787: Add a command to force vrrp state transition to backup
Diffstat (limited to 'scripts/keepalived')
-rwxr-xr-x | scripts/keepalived/VyattaKeepalived.pm | 36 | ||||
-rw-r--r-- | scripts/keepalived/vyatta-clear-vrrp.pl | 305 | ||||
-rwxr-xr-x | scripts/keepalived/vyatta-keepalived.pl | 58 |
3 files changed, 348 insertions, 51 deletions
diff --git a/scripts/keepalived/VyattaKeepalived.pm b/scripts/keepalived/VyattaKeepalived.pm index c9abe49e..6507d8f7 100755 --- a/scripts/keepalived/VyattaKeepalived.pm +++ b/scripts/keepalived/VyattaKeepalived.pm @@ -48,7 +48,7 @@ sub vrrp_log { sub is_running { if (-f $keepalived_pid) { my $pid = `cat $keepalived_pid`; - chomp $pid; + $pid =~ s/\s+$//; # chomp doesn't remove nl my $ps = `ps -p $pid -o comm=`; if (defined($ps) && $ps ne "") { @@ -70,6 +70,7 @@ sub start_daemon { sub stop_daemon { if (is_running()) { my $pid = `cat $keepalived_pid`; + $pid =~ s/\s+$//; # chomp doesn't remove nl system("kill $pid"); vrrp_log("stop_daemon"); } else { @@ -82,7 +83,7 @@ sub restart_daemon { if (VyattaKeepalived::is_running()) { my $pid = `cat $keepalived_pid`; - chomp $pid; + $pid =~ s/\s+$//; # chomp doesn't remove nl system("kill -1 $pid"); vrrp_log("restart_deamon"); } else { @@ -216,6 +217,7 @@ sub snoop_for_master { sub vrrp_state_parse { my ($file) = @_; + $file =~ s/\s+$//; # chomp doesn't remove nl if ( -f $file) { my $line = `cat $file`; chomp $line; @@ -226,4 +228,34 @@ sub vrrp_state_parse { } } +sub vrrp_get_init_state { + my ($intf, $group, $vips, $preempt) = @_; + + my $init_state; + if (VyattaKeepalived::is_running()) { + my @state_files = VyattaKeepalived::get_state_files($intf, $group); + chomp @state_files; + if (scalar(@state_files) > 0) { + my ($start_time, $f_intf, $f_group, $state, $ltime) = + VyattaKeepalived::vrrp_state_parse($state_files[0]); + if ($state eq "master") { + $init_state = 'MASTER'; + } else { + $init_state = 'BACKUP'; + } + return $init_state; + } + # fall through to logic below + } + + if ($preempt eq "false") { + $init_state = 'BACKUP'; + } else { + $init_state = 'MASTER'; + } + + return $init_state; +} + +1; #end of file diff --git a/scripts/keepalived/vyatta-clear-vrrp.pl b/scripts/keepalived/vyatta-clear-vrrp.pl new file mode 100644 index 00000000..be33f2b9 --- /dev/null +++ b/scripts/keepalived/vyatta-clear-vrrp.pl @@ -0,0 +1,305 @@ +#!/usr/bin/perl +# +# Module: vyatta-clear-vrrp.pl +# +# **** License **** +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# This code was originally developed by Vyatta, Inc. +# Portions created by Vyatta are Copyright (C) 2007 Vyatta, Inc. +# All Rights Reserved. +# +# Author: Stig Thormodsrud +# Date: May 2008 +# Description: Script to clear vrrp +# +# **** End License **** +# + +use lib "/opt/vyatta/share/perl5/"; +use VyattaKeepalived; +use Getopt::Long; +use Sys::Syslog qw(:standard :macros); + +use strict; +use warnings; + +my $conf_file = VyattaKeepalived::get_conf_file(); + + +sub keepalived_write_file { + my ($file, $data) = @_; + + open(my $fh, '>', $file) || die "Couldn't open $file - $!"; + print $fh $data; + close $fh; +} + +sub set_instance_inital_state { + my ($instance, $init) = @_; + + if ($init eq "MASTER" and $instance =~ /state \s+ BACKUP/ix) { + if ($instance !~ s/state \s+ BACKUP/state MASTER/ix) { + print "Error: unable to replace BACKUP/MASTER\n"; + } + } elsif ($init eq "BACKUP" and $instance =~ /state \s+ MASTER/ix) { + if ($instance !~ s/state \s+ MASTER/state BACKUP/ix) { + print "Error: unable to replace MASTER/BACKUP\n"; + } + } + return $instance; +} + +my $brace_block; + +sub vrrp_extract_instance { + my ($conf, $instance) = @_; + + # + # regex to find a balanced group of squiggly braces {{{ }}} + # + $brace_block = qr/ + \{ # 1st brace + ( + [^\{\}]+ # anything but brace + | # or + (??{ $brace_block }) # another brace_block + )* + \} # matching brace + /x; + + # + # regex to match instance: + # + # vrrp_instance vyatta-eth1.100-15 { + # state MASTER + # interface eth1 + # virtual_router_id 15 + # virtual_ipaddress { + # 1.1.1.1 + # } + # } + # + my $instance_regex = qr/(vrrp_instance \s+ $instance \s+ $brace_block)/x; + + # + # replace the instance with nothing + # + my $match_instance; + if ($conf =~ s/($instance_regex)//) { + $match_instance = $1; + } else { + return ($conf, undef); + } + + return ($conf, $match_instance); +} + +sub get_vrrp_intf_group { + my @array; + + # + # return an array of hashes that contains all the intf/group pairs + # + + my $config = new VyattaConfig; + $config->setLevel("interfaces ethernet"); + my @eths = $config->listOrigNodes(); + foreach my $eth (@eths) { + my $path = "interfaces ethernet $eth"; + $config->setLevel($path); + if ($config->existsOrig("vrrp")) { + $path = "$path vrrp vrrp-group"; + $config->setLevel($path); + my @groups = $config->listOrigNodes(); + foreach my $group (@groups) { + my %hash; + $hash{'intf'} = $eth; + $hash{'group'} = $group; + $hash{'path'} = "$path $group"; + push @array, {%hash}; + } + } + + $path = "interfaces ethernet $eth"; + $config->setLevel($path); + if ($config->existsOrig("vif")) { + my $path = "$path vif"; + $config->setLevel($path); + my @vifs = $config->listOrigNodes(); + foreach my $vif (@vifs) { + my $vif_intf = $eth . "." . $vif; + my $vif_path = "$path $vif"; + $config->setLevel($vif_path); + if ($config->existsOrig("vrrp")) { + $vif_path = "$vif_path vrrp vrrp-group"; + $config->setLevel($vif_path); + my @groups = $config->listOrigNodes(); + foreach my $group (@groups) { + my %hash; + $hash{'intf'} = $vif_intf; + $hash{'group'} = $group; + $hash{'path'} = "$path $group"; + push @array, {%hash}; + } + } + } + } + } + + return @array; +} + +sub set_inital_state { + my $conf = shift; + + my $new_conf = ''; + + # + # find all intf/groups, extract instance, set init state + # + my @vrrp_instances = get_vrrp_intf_group(); + + foreach my $hash (@vrrp_instances) { + my $intf = $hash->{'intf'}; + my $group = $hash->{'group'}; + my $instance = "vyatta-" . "$intf" . "-" . "$group"; + my ($tmp_conf, $match_instance) = + vrrp_extract_instance($conf, $instance); + if (defined $match_instance) { + my $init = VyattaKeepalived::vrrp_get_init_state($intf, $group, + "", "false"); + $match_instance = set_instance_inital_state($match_instance, $init); + $new_conf .= $match_instance . "\n\n"; + } + } + + return $new_conf; +} + + +# +# main +# +my ($action, $vrrp_intf, $vrrp_group); + +GetOptions("vrrp-action=s" => \$action, + "intf=s" => \$vrrp_intf, + "group=s" => \$vrrp_group); + +if (! defined $action) { + print "no action\n"; + exit 1; +} + +openlog($0, "", LOG_USER); +my $login = getlogin(); + +# +# clear_process +# +if ($action eq "clear_process") { + syslog("warning", "clear vrrp process requested by $login"); + if (VyattaKeepalived::is_running()) { + print "Restarting VRRP...\n"; + VyattaKeepalived::restart_daemon(VyattaKeepalived::get_conf_file()); + } else { + print "Starting VRRP...\n"; + VyattaKeepalived::start_daemon(VyattaKeepalived::get_conf_file()); + } + exit 0; +} + +# +# clear_master +# +if ($action eq "clear_master") { + + # + # The kludge here to force a vrrp instance to switch from master to + # backup is to read the keepalived config, remove the instance to be + # cleared, signal the daemon to reread it's config. This will cause + # keepalived to see the old instance missing and send a priorty 0 + # advert to cause the backup to immediately take over master. Once + # that is done we put back the orginal config and signal the daemon + # again. Note: if the instance if preempt=true, then it may immediately + # try to become master again. + # + + if (! defined $vrrp_intf || ! defined $vrrp_group) { + print "must include interface & group\n"; + exit 1; + } + + my $instance = "vyatta-" . "$vrrp_intf" . "-" . "$vrrp_group"; + my $state_file = VyattaKeepalived::get_state_file($vrrp_intf, $vrrp_group); + if (! -f $state_file) { + print "Invalid interface/group [$vrrp_intf][$vrrp_group]\n"; + exit 1; + } + + my ($start_time, $intf, $group, $state, $ltime) = + VyattaKeepalived::vrrp_state_parse($state_file); + if ($state ne "master") { + print "vrrp group $vrrp_group on $vrrp_intf is already in backup\n"; + exit 1; + } + + syslog("warning", "clear vrrp master [$instance] requested by $login"); + VyattaKeepalived::vrrp_log("vrrp clear_master $vrrp_intf $vrrp_group"); + + # should add a file lock + local($/, *FILE); # slurp mode + open FILE, "<", $conf_file or die "Couldn't open $conf_file\n"; + my $conf = <FILE>; + close FILE; + + my ($new_conf, $match_instance) = vrrp_extract_instance($conf, $instance); + if ($match_instance !~ /nopreempt/) { + print "Warning: $instance is in preempt mode"; + print " and may retake master\n"; + } + $match_instance = set_instance_inital_state($match_instance, "BACKUP"); + + # + # need to set the correct initial state for the remaining instances + # + $new_conf = set_inital_state($new_conf); + + # + # create the temporary config file + # + my $tmp_conf_file = $conf_file . ".$$"; + keepalived_write_file($tmp_conf_file, $new_conf); + + my $conf_file_bak = $conf_file . ".bak"; + system("mv $conf_file $conf_file_bak"); + system("cp $tmp_conf_file $conf_file"); + + VyattaKeepalived::restart_daemon($conf_file); + + print "Forcing $vrrp_intf-$group to BACKUP...\n"; + sleep(3); + + # + # add modified instance back and restart + # + $new_conf .= "\n" . $match_instance . "\n"; + + keepalived_write_file($conf_file, $new_conf); + VyattaKeepalived::restart_daemon($conf_file); + + system("rm $conf_file_bak $tmp_conf_file"); + exit 0; +} + +exit 0; + +# end of file diff --git a/scripts/keepalived/vyatta-keepalived.pl b/scripts/keepalived/vyatta-keepalived.pl index ac92ca8e..e40c737a 100755 --- a/scripts/keepalived/vyatta-keepalived.pl +++ b/scripts/keepalived/vyatta-keepalived.pl @@ -36,33 +36,6 @@ my $conf_file = VyattaKeepalived::get_conf_file(); my %HoA_sync_groups; -sub vrrp_get_init_state { - my ($intf, $group, $vips, $preempt) = @_; - - my $init_state; - if (VyattaKeepalived::is_running()) { - my @state_files = VyattaKeepalived::get_state_files($intf, $group); - if (scalar(@state_files) > 0) { - my ($start_time, $f_intf, $f_group, $state, $ltime) = - VyattaKeepalived::vrrp_state_parse($state_files[0]); - if ($state eq "master") { - $init_state = 'MASTER'; - } else { - $init_state = 'BACKUP'; - } - return $init_state; - } - # fall through to logic below - } - - if ($preempt eq "false") { - $init_state = 'BACKUP'; - } else { - $init_state = 'MASTER'; - } - - return $init_state; -} sub keepalived_get_values { my ($intf, $path) = @_; @@ -132,7 +105,9 @@ sub keepalived_get_values { } $output .= "vrrp_instance $vrrp_instance \{\n"; - my $init_state = vrrp_get_init_state($intf, $group, $vips[0], $preempt); + my $init_state; + $init_state = VyattaKeepalived::vrrp_get_init_state($intf, $group, + $vips[0], $preempt); $output .= "\tstate $init_state\n"; $output .= "\tinterface $intf\n"; $output .= "\tvirtual_router_id $group\n"; @@ -151,12 +126,12 @@ sub keepalived_get_values { $output .= "\t\t$vip\n"; } $output .= "\t\}\n"; - $output .= "\tnotify_master "; - $output .= "\"$state_transition_script master $intf $group $run_master_script @vips\" \n"; - $output .= "\tnotify_backup "; - $output .= "\"$state_transition_script backup $intf $group $run_backup_script @vips\" \n"; - $output .= "\tnotify_fault "; - $output .= "\"$state_transition_script fault $intf $group $run_fault_script @vips\" \n"; + $output .= "\tnotify_master \"$state_transition_script master "; + $output .= "$intf $group $run_master_script @vips\" \n"; + $output .= "\tnotify_backup \"$state_transition_script backup "; + $output .= "$intf $group $run_backup_script @vips\" \n"; + $output .= "\tnotify_fault \"$state_transition_script fault "; + $output .= "$intf $group $run_fault_script @vips\" \n"; $output .= "\}\n\n"; } @@ -408,21 +383,6 @@ if ($action eq "delete") { exit 0; } -if ($action eq "clear") { - if (VyattaKeepalived::is_running()) { - print "Restarting VRRP...\n"; - VyattaKeepalived::restart_daemon(VyattaKeepalived::get_conf_file()); - } else { - print "Starting VRRP...\n"; - VyattaKeepalived::start_daemon(VyattaKeepalived::get_conf_file()); - } - exit 0; -} - exit 0; # end of file - - - - |