#!/usr/bin/perl

###############################################################################
###############################################################################
##
##  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
##  Copyright (C) 2004-2007 Red Hat, Inc.  All rights reserved.
##  
##  This copyrighted material is made available to anyone wishing to use,
##  modify, copy, or redistribute it subject to the terms and conditions
##  of the GNU General Public License v.2.
##
##  VMWare ESX Server fencing support by Zach Lowry <zach@zachlowry.net>
##
###############################################################################
###############################################################################

use strict;
use Getopt::Std;
use VMware::VmPerl;
use VMware::VmPerl::VM;
use VMware::VmPerl::Server;
use VMware::VmPerl::ConnectParams;
use vars qw( $opt_L $opt_v $opt_V $opt_h $opt_T $opt_n $opt_o $opt_p $opt_P $opt_S $opt_l $opt_a $opt_q $vm_product $vm_platform $vm_build $vm_version_major $vm_version_minor $vm_version_revision );

# Get the program name from $0 and strip directory names
$_=$0;
s/.*\///;
my $pname = $_;

# Change these if the text returned by your equipment is different.
# Test by running script with options -t -v and checking /var/log/cluster/fence_vmware.log

my $immediate = 'immediate'; # # Or 'delayed' - action string prefix on menu

my $max_open_tries = 3;      # How many attempts to make.
my $open_wait = 5;           # Seconds to wait between each attempt
my $debuglog = '/var/log/cluster/fence_vmware.log';# Location of debugging log when in verbose mode
my $powerop_mode = VM_POWEROP_MODE_HARD;
$opt_o = 'Reboot';           # Default fence action.  


my $logged_in = 0;

my $vm = VMware::VmPerl::VM::new();
my $server = VMware::VmPerl::Server::new();

# WARNING!! Do not add code bewteen "#BEGIN_VERSION_GENERATION" and
# "#END_VERSION_GENERATION"  It is generated by the Makefile

#BEGIN_VERSION_GENERATION
$RELEASE_VERSION="2.0";
$REDHAT_COPYRIGHT=("Copyright (C) Red Hat, Inc.  2004-2007  All rights reserved.");
$BUILD_DATE="(built Thu Dec 17 20:04:18 UTC 2009)";
#END_VERSION_GENERATION

sub usage 
{
	print "Usage:\n";
	print "\n";
	print "$pname [options]\n";
	print "\n";
	print "Options:\n";
	print "  -a <ip>:<port>   IP address or hostname of VMware ESX Server\n";
	print "  -h               usage\n";
	print "  -l <name>        Login name\n";
	print "  -p <string>      Login password\n";
	print "  -S <path>        Script to run to retrieve login password\n";
	print "  -n <name>        Name of VM to change \n";
	print "  -o <string>      Action: Reboot (default), Off or On\n";
	print "  -q               quiet mode\n";
	print "  -T               Test mode (cancels action)\n";
	print "  -V               version\n";
	print "  -v               Log to file /var/log/cluster/fence_vmware.log\n";
	print "  -L               List VMs on Server\n";
	
	exit 0;
}

sub fail
{
	my ($msg)=@_;
	print $msg."\n" unless defined $opt_q;

	if (defined $vm)
	{
		# make sure we don't get stuck in a loop due to errors

		logout() if $logged_in;
		undef $vm;
	}
	exit 1;
}

sub fail_usage
{
	my ($msg)=@_;
	print STDERR $msg."\n" if $msg;
	print STDERR "Please use '-h' for usage.\n";
	exit 1;
}

sub version
{
	print "$pname $RELEASE_VERSION $BUILD_DATE\n";
	print "$REDHAT_COPYRIGHT\n" if ( $REDHAT_COPYRIGHT );
	exit 0;
}


sub login
{
	my $connect_params = VMware::VmPerl::ConnectParams::new($opt_a, $opt_P, $opt_l, $opt_p);
	for (my $i=0; $i<$max_open_tries; $i++)
	{
		if (defined $opt_L) {
			if (! $server->connect($connect_params)) {
				my ($error_number, $error_string) = $server->get_last_error(); 
				fail "$error_number, $error_string";
			}
			return;
		} else {
			if (! $vm->connect($connect_params, $opt_n)) {
				my ($error_number, $error_string) = $vm->get_last_error(); 
				fail "$error_number, $error_string";
			}
			return;
		}
	}
	if (defined $opt_L) {
		fail "failed: connect failed: ". $server->get_last_error() ."\n";
	} else {
		fail "failed: connect failed: ". $vm->get_last_error() ."\n";
	}
}

# Determine if the switch is a working state.  Also check to make sure that 
# the switch has been specified in the case that there are slave switches
# present.  This assumes that we are at the main menu.
sub identify_vmware
{
	if (! defined $opt_L) {
		$vm_product = $vm->get_product_info(VM_PRODINFO_PRODUCT);
		$vm_platform = $vm->get_product_info(VM_PRODINFO_PLATFORM);
		$vm_build = $vm->get_product_info(VM_PRODINFO_BUILD);
		$vm_version_major = $vm->get_product_info(VM_PRODINFO_VERSION_MAJOR);
		$vm_version_minor = $vm->get_product_info(VM_PRODINFO_VERSION_MINOR);
		$vm_version_revision = $vm->get_product_info(VM_PRODINFO_VERSION_REVISION);
	}
}


sub logout 
{
	undef $vm, $server;
}


sub action
{
	if (defined $opt_L) {
		foreach my  $name ($server->registered_vm_names()) {
			print "$name\n";
		}
		logout(); 

		exit 0;
	}
	if (defined $opt_T) {
		print "success: test outlet $opt_n $opt_o\n" unless defined $opt_q; 
		logout(); 

		exit 0;
	} elsif ( $vm->is_connected() ) {
		my $cur_state = $vm->get_execution_state();
		if ( $opt_o =~ /Reboot/i ) {
			$vm->reset($powerop_mode);
		} elsif ( $opt_o =~ /On/i ) {
			$vm->start($powerop_mode);
		} elsif ( $opt_o =~ /Off/i ) {
			$vm->stop($powerop_mode);
		}
		if ($_) {
			my ($error_number, $error_string) = $vm->get_last_error(); 
			if ($error_number != 0) {
				fail "$error_number, $error_string";
			} else {
				print "success: outlet $opt_n $opt_o\n" unless defined $opt_q; 
			}
			logout();

			exit 0;
		}
	} 

	fail "failed: unrecognised action response\n";
}


sub get_options_stdin
{
	my $opt;
	my $line = 0;
	my $in;
	while( defined($in = <>) )
	{
		$_ = $in;
		chomp;

		# strip leading and trailing whitespace
		s/^\s*//;
		s/\s*$//;

		# skip comments
		next if /^#/;
	
		$line+=1;
		$opt=$_;
		next unless $opt;

		my ($name,$val)=split /\s*=\s*/, $opt;

		if ( $name eq "" )
		{
			print STDERR "parse error: illegal name in option $line\n";
			exit 2;
		} 
		# DO NOTHING -- this field is used by fenced 
		elsif ($name eq "agent" ) 
		{
		} 
		elsif ($name eq "ipaddr" ) 
		{
			$opt_a = $val;
		} 
		elsif ($name eq "login" ) 
		{
			$opt_l = $val;
		} 
		elsif ($name eq "option" ) 
		{
			$opt_o = $val;
		} 
		elsif ($name eq "passwd" ) 
		{
			$opt_p = $val;
		}
		elsif ($name eq "passwd_script" )
		{
			$opt_S = $val;
		}
		elsif ($name eq "port" ) 
		{
			$opt_n = $val;
		} 
		elsif ($name eq "switch" ) 
		{
			$opt_P = $val;
		} 
		elsif ($name eq "test" ) 
		{
			$opt_T = $val;
		} 
		elsif ($name eq "verbose" ) 
		{
			$opt_v = $val;
		} 
		# Excess name/vals will fail
		else 
		{
			fail "parse error: unknown option \"$opt\"";
		}
	}
}
		

sub connect_error
{
	fail "failed: connect returned: ".$vm->get_last_error()."\n";
}


### MAIN #######################################################

if (@ARGV > 0) {
	getopts("a:hl:n:o:p:S:qTvVL") || fail_usage ;
	
	usage if defined $opt_h;
	version if defined $opt_V;

	fail_usage "Unkown parameter." if (@ARGV > 0);

	fail_usage "No '-a' flag specified." unless defined $opt_a;
	fail_usage "No '-n' flag specified." unless defined $opt_n or defined $opt_L;
	fail_usage "No '-l' flag specified." unless defined $opt_l;

	if (defined $opt_S) {
		$pwd_script_out = `$opt_S`;
		chomp($pwd_script_out);
		if ($pwd_script_out) {
			$opt_p = $pwd_script_out;
		}
	}

	fail_usage "No '-p' or '-S' flag specified." unless defined $opt_p;
	fail_usage "Unrecognized action '$opt_o' for '-o' flag"
		unless $opt_o =~ /^(Off|On|Reboot)$/i;

	($opt_a, $opt_P) = split(/:/, $opt_a);
	fail_usage "No port number specified." unless defined $opt_P;

} else {
	get_options_stdin();

	fail "failed: no IP address" unless defined $opt_a;
	fail "failed: no vm name" unless defined $opt_n;
	fail "failed: no login name" unless defined $opt_l;

	if (defined $opt_S) {
		$pwd_script_out = `$opt_S`;
		chomp($pwd_script_out);
		if ($pwd_script_out) {
			$opt_p = $pwd_script_out;
		}
	}

	fail "failed: no password" unless defined $opt_p;
	fail "failed: unrecognized action: $opt_o"
		unless $opt_o =~ /^(Off|On|Reboot)$/i;
} 

&login;

&identify_vmware;

&action;

exit 0;


