#!/usr/bin/perl -w
# stonith_multi
#
# Description: STONITH agent
# Author: Chris Picton: Initial version
#
# This is intended to be used as an STONITH agent for fencing
#   It can operate on multiple stonith devices - if the first fails, it tries
#   the others in order
#
# It takes two parameters on the command line
#   <command> <node>
#
#   The command is one of
#      gethosts
#      on
#      off
#      reset
#      status
#
#   In addition, environment variables will be passed in 
#   from heartbeat settngs
#
#      agent[1-9]                - Name of stonith scripts to try in order
#      agent[1-9]_paramname[1-9] - Parameters to pass to each script 
#      agent[1-9]_paramval[1-9]  - Parameters to pass to each script 
#      debug                     - Extra debug?
#########################################################################
use strict;
use Data::Dumper;
use Sys::Syslog;

my $COMMAND = $ARGV[0];
my $NODE    = $ARGV[1];

my $NUMAGENTS=3;
my $NUMPARAMS=7;

my $DEBUG=$ENV{'debug'};
$DEBUG = 0 if !defined $DEBUG;

openlog "stonith_multi", "pid", "user";

if ($DEBUG == 2) {
   foreach my $k (keys %ENV) {
      logger("$k = $ENV{$k}");
   }
}

if (!defined $COMMAND) {
   logger("ERROR: Command Required");
   exit(1);
}

$NODE = "" if !defined $NODE;

my $agent;

my $params;
foreach my $var (keys %ENV) {
   my $val = $ENV{$var};
   if ($var =~ /^agent(\d)$/) {
      my $script = "/usr/lib64/stonith/plugins/$val";
      if (! -x "$script") {
         logger("ERROR: Cannot find script $script");
         next;
      }
      $agent->{$1}->{'name'} = $val;
      $agent->{$1}->{'script'} = $script;
      next;
   }
   if ($var =~ /^agent(\d)_param(\d)name$/) {
      $agent->{$1}->{'params'}->{$2}->{'name'} = $val;
      next;
   }
   if ($var =~ /^agent(\d)_param(\d)val$/) {
      $agent->{$1}->{'params'}->{$2}->{'val'} = $val;
      next;
   }
}



##############################################################################
##############################################################################
sub setenv {
   my $p = shift;
   
   foreach my $paramid (keys %$p) {
      my $name = $p->{$paramid}->{'name'};
      my $val = $p->{$paramid}->{'val'};
      logger("$paramid: $name -> $val") if $DEBUG;
      $ENV{$name} = $val;
   }
}

# at least one must succeed
sub doaction {
   my $action = shift;
   my $RV = 1;
   my $h;
   foreach my $agentid (keys %$agent) {
      my $name = $agent->{$agentid}->{'name'};
      my $script = $agent->{$agentid}->{'script'};
      
      setenv($agent->{$agentid}->{'params'});
      
      logger("$name $action $NODE - Running");
      my @out = readpipe "$script $action '$NODE' 2>&1";
      my $rv = $? >> 8;
      print "OUTPUT: @out" if $DEBUG;
      logger("$name $action $NODE - Status $rv") if $DEBUG;
      if ($rv != 0) {
         logger("ERROR: $name: $action of $name failed: $rv");
         next;
      }
      if ($rv == 0) {
         logger("Success");
         return 0;
      }
      $RV = $rv;
   }
   return $RV;
}

# at least one must succeed, but run them all
sub dostatus {
   my $action = 'status';
   my $RV = 1;
   my $h;
   foreach my $agentid (keys %$agent) {
      my $name = $agent->{$agentid}->{'name'};
      my $script = $agent->{$agentid}->{'script'};
      
      setenv($agent->{$agentid}->{'params'});
      
      logger("$name $action $NODE - Running") if $DEBUG;
      my @out = readpipe "$script $action '$NODE' 2>&1";
      my $rv = $? >> 8;
      print "OUTPUT: @out\n" if $DEBUG;
      logger("$name $action $NODE - Status $rv") if $DEBUG;
      if ($rv != 0) {
         logger("ERROR: $name: $action of $name failed: $rv");
         next;
      }
      $RV = 0;
   }
   return $RV;
}


sub logger {
   my $log = shift;
   print STDERR "$log\n" if $DEBUG;
   syslog("info", "$log");   
}



# Return intersection of all hosts returned by all agents
if ($COMMAND eq "gethosts") {
   my $RV = -1;
   my $h;
   foreach my $agentid (keys %$agent) {
      my $name = $agent->{$agentid}->{'name'};
      my $script = $agent->{$agentid}->{'script'};
      setenv($agent->{$agentid}->{'params'});
      
      print STDERR "Getting hosts list for $name\n" if $DEBUG;
      
      my @out = readpipe "$script gethosts";
      my $rv = $? >> 8;
      if ($rv != 0) {
         logger("ERROR: $name: failed to get hosts: $rv");
         next;
      }
      chomp(@out);
      print STDERR "Hosts = @out\n" if $DEBUG;
      map { $h->{$agentid}->{$_} = 1 } @out;
   }
   
   my $validhosts;
   
   foreach my $agentid (keys %$h) {
      foreach my $host (keys %{$h->{$agentid}}) {
         my $valid = 1;
         foreach my $aid2 (keys %$h) {
            if (!defined $h->{$aid2}->{$host}) {
               $valid = 0;
            }
         }
         if ($valid == 1) {
            $validhosts->{$host} = 1;
         }
      }
   }
   
   foreach my $host (sort keys %$validhosts) {
      print "$host\n";
   }
   exit(0);
}

if ($COMMAND eq "reset") {
   my $rv;
   $rv = doaction("off");
   if ($rv != 0) {
      logger("ERROR: Failed to turn off");
      exit(1);
   }
   sleep(1);
   $rv = doaction("on");
   if ($rv != 0) {
      logger("ERROR: Failed to turn on");
      exit(1);
   }
   exit 0;   
}

if ($COMMAND eq "off") {
   my $rv;

   $rv = doaction("off");
   if ($rv != 0) {
      logger("ERROR: Failed to turn off");
      exit(1);
   }
   exit(0);
}

if ($COMMAND eq "on") {
   my $rv;

   $rv = doaction("on");
   if ($rv != 0) {
      logger("ERROR: Failed to turn on");
      exit(1);
   }
   exit 0;   
}

if ($COMMAND eq "status") {
   my $rv;
   $rv = dostatus();
   if ($rv != 0) {
      logger("ERROR: Failed to get status from any device");
      exit(1);
   }
   exit(0);
}

if ($COMMAND eq "getconfignames") {
   print "\n";
   exit(0);
}

if ($COMMAND eq "getinfo-devid") {
   print "stonith_multi\n";
   exit(0);
}

if ($COMMAND eq "getinfo-devname") {
   print "stonith_multi\n";
   exit(0);
}

if ($COMMAND eq "getinfo-devdescr") {
   print "STONITH agent to handle failover between different STONIGH agents\n";
   exit(0);
}

if ($COMMAND eq "getinfo-devurl") {
   print "http://www.ecntelecoms.com\n";
   exit(0);
}

if ($COMMAND eq "getinfo-xml") {
   print "<parameters>\n";
   print " <parameter name='debug' unique='1' required='0'>\n";
   print "  <content type='string' />\n";
   print "  <shortdesc lang='en'>Debug enabled?</shortdesc>\n";
   print " </parameter>\n";
   for my $i (1 .. $NUMAGENTS) {
      print " <parameter name='agent$i' unique='1' required='0'>\n";
      print "  <content type='string' />\n";
      print "  <shortdesc lang='en'>STONITH Agent $i</shortdesc>\n";
      print " </parameter>\n";
      for my $j (1 .. $NUMPARAMS) {
         print " <parameter name='agent${i}_param${j}name' unique='1' required='0'>\n";
         print "  <content type='string' />\n";
         print "  <shortdesc lang='en'>STONITH Agent $i Parameter $j name</shortdesc>\n";
         print " </parameter>\n";
         print " <parameter name='agent${i}_param${j}val' unique='1' required='0'>\n";
         print "  <content type='string' />\n";
         print "  <shortdesc lang='en'>STONITH Agent $i Parameter $j value</shortdesc>\n";
         print " </parameter>\n";
      }
   }
   print "</parameters>\n";

   exit(0);
}
