public
Last active

Fencing agent for Amazon EC2

  • Download Gist
gistfile1.txt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318
#!/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

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.