Created
April 26, 2011 10:32
-
-
Save beekhof/942090 to your computer and use it in GitHub Desktop.
Fencing agent for Amazon EC2
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
description=" | |
fence_ec2 is an I/O Fencing agent which can be used with Amazon EC2 instances. | |
In order to function, the agent needs the private key and cert used by the Amazon EC2 API. | |
API functions used by this agent: | |
- ec2-describe-tags | |
- ec2-describe-instances | |
- ec2-stop-instances | |
- ec2-start-instances | |
- ec2-reboot-instances | |
If the uname used by the cluster node is any of: | |
- Public DNS name (or part there of), | |
- Private DNS name (or part there of), | |
- Instance ID (eg. i-4f15a839) | |
- Contents of tag associated with the instance | |
then the agent should be able to automatically discover the instances it can control. | |
If the tag containing the uname is not [Name], then it will need to be specified using the [tag] option. | |
" | |
# | |
# Copyright (c) 2011 Andrew Beekhof | |
# All Rights Reserved. | |
# | |
# This program is free software; you can redistribute it and/or modify | |
# it under the terms of version 2 of the GNU General Public License as | |
# published by the Free Software Foundation. | |
# | |
# This program is distributed in the hope that it would be useful, but | |
# WITHOUT ANY WARRANTY; without even the implied warranty of | |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | |
# | |
# Further, this software is distributed without any warranty that it is | |
# free of the rightful claim of any third person regarding infringement | |
# or the like. Any license provided herein, whether implied or | |
# otherwise, applies only to this software file. Patent licenses, if | |
# any, provided herein do not apply to combinations of this program with | |
# other software, or any other product whatsoever. | |
# | |
# You should have received a copy of the GNU General Public License | |
# along with this program; if not, write the Free Software Foundation, | |
# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. | |
# | |
####################################################################### | |
quiet=0 | |
port="" | |
instance_not_found=0 | |
unknown_are_stopped=0 | |
action="reset" # Default fence action | |
ec2_tag="Name" # EC2 Tag containing the instance's uname | |
ec2_key="" # EC2 Private Key | |
ec2_cert="" # EC2 Cert | |
ec2_region="us-east-1" # EC2 Region | |
if [ -z "$EC2_HOME" ]; then | |
EC2_HOME="$HOME/.ec2" | |
fi | |
function usage() | |
{ | |
cat <<EOF | |
`basename $0` - A fencing agent for Amazon EC2 instances | |
$description | |
Usage: `basename $0` -o|--action [-n|--port] [options] | |
Options: | |
-h, --help This text | |
-V, --version Version information | |
-q, --quiet Reduced output mode | |
Commands: | |
-o, --action Action to perform: on|off|reboot|status|monitor | |
-n, --port The name of a machine/instance to control/check | |
Additional Options: | |
-e, --ec2-home Location of Amazon EC2 command line tools | |
-k, --private-key The private key to use when constructing requests to Amazon EC2 | |
-c, --cert The X.509 certificate to use when constructing requests to Amazon EC2 | |
-r, --region The Amazon region for which the device should control instances (defaults to us-east-1) | |
-t, --tag Name of the tag containing the instance's uname | |
Dangerous options: | |
-U, --unknown-are-stopped Assume any unknown instance is safely stopped | |
EOF | |
exit 0; | |
} | |
function metadata() | |
{ | |
cat <<EOF | |
<?xml version="1.0" ?> | |
<resource-agent name="fence_ec2" shortdesc="Fencing agent for Amazon EC2 instances" > | |
<longdesc> | |
$description | |
</longdesc> | |
<parameters> | |
<parameter name="action" unique="1" required="1"> | |
<getopt mixed="-o, --action=[action]" /> | |
<content type="string" default="reboot" /> | |
<shortdesc lang="en">Fencing Action</shortdesc> | |
</parameter> | |
<parameter name="port" unique="1" required="1"> | |
<getopt mixed="-n, --port=[port]" /> | |
<content type="string" /> | |
<shortdesc lang="en">The name/id/tag of a instance to control/check</shortdesc> | |
</parameter> | |
<parameter name="ec2-home" unique="1" required="1"> | |
<getopt mixed="-e, --ec2-home=[directory]" /> | |
<content type="string" default="~/.ec2" /> | |
<shortdesc lang="en">Location of Amazon EC2 command line tools</shortdesc> | |
</parameter> | |
<parameter name="private-key" unique="1" required="1"> | |
<getopt mixed="-k, --private-key=[filename]" /> | |
<content type="string" default="$ec2-home/pk-*.pem" /> | |
<shortdesc lang="en">The private key to use when constructing requests to Amazon EC2</shortdesc> | |
</parameter> | |
<parameter name="cert" unique="1" required="1"> | |
<getopt mixed="-c, --cert=[filename]" /> | |
<content type="string" default="$ec2-home/cert-*.pem" /> | |
<shortdesc lang="en">The X.509 certificate to use when constructing requests to Amazon EC2</shortdesc> | |
</parameter> | |
<parameter name="region" unique="1" required="1"> | |
<getopt mixed="-r, --region=[region]" /> | |
<content type="string" default="us-east-1" /> | |
<shortdesc lang="en">The Amazon region for which the device should control instances</shortdesc> | |
</parameter> | |
<parameter name="tag" unique="1" required="1"> | |
<getopt mixed="-t, --tag=[tag]" /> | |
<content type="string" default="Name" /> | |
<shortdesc lang="en">Name of the tag containing the instances uname</shortdesc> | |
</parameter> | |
<parameter name="unknown-are-stopped" unique="1" required="1"> | |
<getopt mixed="-U, --unknown-are-stopped" /> | |
<content type="string" default="false" /> | |
<shortdesc lang="en">DANGER: Assume any unknown instance is safely stopped</shortdesc> | |
</parameter> | |
</parameters> | |
<actions> | |
<action name="on" /> | |
<action name="off" /> | |
<action name="reboot" /> | |
<action name="status" /> | |
<action name="list" /> | |
<action name="monitor" /> | |
<action name="metadata" /> | |
</actions> | |
</resource-agent> | |
EOF | |
exit 0; | |
} | |
function instance_for_port() | |
{ | |
port=$1; shift | |
# Look for port name -n in the INSTANCE data | |
instance=`ec2-describe-instances $* | grep INSTANCE.*$port | awk '{print $2}'` | |
if [ -z $instance ]; then | |
# Look for port name -n in the Name TAG | |
instance=`ec2-describe-tags $* | grep TAG.*$ec2_tag.*$port | awk '{print $3}'` | |
fi | |
if [ -z $instance ]; then | |
instance_not_found=1 | |
instance=$port | |
fi | |
echo $instance | |
} | |
TEMP=`getopt -o qVo:e:k:c:r:n:t:U --long version,help,region:,action:,port:,option:,ec2-home:,private-key:,cert:,tag:,quiet,unknown-are-stopped \ | |
-n 'fence_ec2' -- "$@"` | |
if [ $? != 0 ];then | |
usage | |
exit 1 | |
fi | |
# Note the quotes around `$TEMP': they are essential! | |
eval set -- "$TEMP" | |
if [ -z $1 ]; then | |
# If there are no command line args, look for options from stdin | |
while read line; do | |
case $line in | |
option=*|action=*) action=`echo $line | sed s/.*=//`;; | |
port=*) port=`echo $line | sed s/.*=//`;; | |
ec2-home=*) EC2_HOME=`echo $line | sed s/.*=//`;; | |
private-key=*) ec2_key=`echo $line | sed s/.*=//`;; | |
cert=*) ec2_cert=`echo $line | sed s/.*=//`;; | |
region=*) ec2_region=`echo $line | sed s/.*=//`;; | |
tag=*) ec2_tag=`echo $line | sed s/.*=//`;; | |
quiet*) quiet=1;; | |
unknown-are-stopped*) unknown_are_stopped=1;; | |
--);; | |
*) echo "Invalid command: $line";; | |
esac | |
done | |
fi | |
while true ; do | |
case "$1" in | |
-o|--action|--option) action=$2; shift; shift;; | |
-n|--port) port=$2; shift; shift;; | |
-e|--ec2-home) EC2_HOME=$2; shift; shift;; | |
-k|--private-key) ec2_key=$2; shift; shift;; | |
-c|--cert) ec2_cert=$2; shift; shift;; | |
-r|--region) ec2_region=$2; shift; shift;; | |
-t|--tag) ec2_tag=$2; shift; shift;; | |
-U|--unknown-are-stopped) unknown_are_stopped=1; shift;; | |
-q|--quiet) quiet=1; shift;; | |
-V|--version) echo "1.0.0"; exit 0;; | |
--help|-h) | |
usage; | |
exit 0;; | |
--) shift ; break ;; | |
*) echo "Unknown option: $1. See --help for details."; exit 1;; | |
esac | |
done | |
export EC2_HOME | |
PATH=$PATH:$EC2_HOME/bin | |
if [ -z "$JAVA_HOME" ]; then | |
java=`which java` | |
while [ -L "$java" ]; do | |
java=`/bin/ls -l $java | awk '{print $11}'` | |
done | |
export JAVA_HOME=`dirname $java`/.. | |
fi | |
if [ -z "$ec2_key" ]; then | |
ec2_key=`ls $EC2_HOME/pk-*.pem`; | |
fi | |
if [ -z "$ec2_cert" ]; then | |
ec2_cert=`ls $EC2_HOME/cert-*.pem`; | |
fi | |
options="--region $ec2_region --private-key $ec2_key --cert $ec2_cert" | |
action=`echo $action | tr 'A-Z' 'a-z'` | |
instance="" | |
if [ ! -z "$port" ]; then | |
instance=`instance_for_port $port $options` | |
fi | |
case $action in | |
reboot|reset) | |
if [ $unknown_are_stopped = 1 -a $instance_not_found ]; then | |
: nothing we _can_ do | |
echo "Assuming unknown instance $instance is already off, cannot restart" | |
else | |
ec2-reboot-instances $options $instance | |
fi | |
;; | |
poweron|on) | |
ec2-start-instances $options $instance | |
;; | |
poweroff|off) | |
if [ $unknown_are_stopped = 1 -a $instance_not_found ]; then | |
: nothing to do | |
echo "Assuming unknown instance $instance is already off" | |
else | |
ec2-stop-instances $options $instance | |
fi | |
;; | |
monitor) | |
# Is the device ok? | |
ec2-describe-instances | grep INSTANCE &> /dev/null | |
;; | |
hostlist|list) | |
# List of names we know about | |
ec2-describe-instances $options | awk -v tag_pat="^TAG\tinstance\t.*\t$ec2_tag" -F '\t' '{ | |
if (/^INSTANCE.*pending/) { printf "%s\n", $2 } | |
else if (/^INSTANCE.*stopped/) { printf "%s\n", $2 } | |
else if (/^INSTANCE/) { printf "%s\n%s\n%s\n", $2, $4, $5 } | |
else if ( $1"\t"$2"\t"$3"\t"$4 ~ tag_pat ) { printf "%s\n", $5 } | |
}' | sort -u | |
;; | |
stat|status) | |
# List of instances and their current status | |
if [ $unknown_are_stopped = 1 -a $instance_not_found ]; then | |
echo "$instance stopped (unknown)" | |
else | |
ec2-describe-instances $options $instance | awk '{ | |
if (/^INSTANCE.*pending/) { printf "%s %s\n", $2, $4 } | |
else if (/^INSTANCE.*stopped/) { printf "%s %s\n", $2, $4 } | |
else if (/^INSTANCE/) { printf "%s %s %s %s\n", $2, $6, $4, $5 } | |
}' | |
fi | |
;; | |
metadata) | |
metadata | |
;; | |
*) echo "Unknown action: $action"; exit 1;; | |
esac | |
status=$? | |
if [ $quiet -eq 1 ]; then | |
: nothing | |
elif [ $status -eq 0 ]; then | |
echo "Operation $action passed" | |
else | |
echo "Operation $action failed: $status" | |
fi | |
exit $status |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment