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
|
#!/bin/bash
VERBOSITY=0
TEMP_D=""
TAB=$'\t'
OIFS=$IFS
error() { echo "$@" 1>&2; }
errorp() { printf "$@" 1>&2; }
fail() { [ $# -eq 0 ] || error "$@"; exit 1; }
failp() { [ $# -eq 0 ] || errorp "$@"; exit 1; }
Usage() {
cat <<EOF
Usage: ${0##*/} [ options ]
Reap (terminate) running instances older than AGE
options:
--regions R operate only in listed regions
default (all regions in ec2-list-all-regions)
-v|--verbose increase verbosity
--dry-run only list what would be done
-m|--max-age M terminate instances older than M seconds
EOF
}
bad_Usage() { Usage 1>&2; [ $# -eq 0 ] || error "$@"; exit 1; }
cleanup() {
local pids
pids=$(jobs -pr)
if [ -n "$pids" ]; then
pids=$(echo $pids)
error "killing and waiting on child pids: $pids"
kill -SIGTERM $pids
wait $pids
fi
[ -z "${TEMP_D}" -o ! -d "${TEMP_D}" ] || rm -Rf "${TEMP_D}"
}
debugp() {
local level=${1}; shift;
[ "${level}" -gt "${VERBOSITY}" ] && return
printf "$@" 1>&2;
}
debug() {
local level=${1}; shift;
[ "${level}" -gt "${VERBOSITY}" ] && return
echo "$(date): ${@}" 1>&2
}
short_opts="hmrv"
long_opts="help,dry-run,max-age:,regions:,verbose"
getopt_out=$(getopt --name "${0##*/}" \
--options "${short_opts}" --long "${long_opts}" -- "$@") &&
eval set -- "${getopt_out}" ||
bad_Usage
## <<insert default variables here>>
regions=( )
max_age=""
dry_run=0
while [ $# -ne 0 ]; do
cur=${1}; next=${2};
case "$cur" in
-h|--help) Usage ; exit 0;;
-r|--regions) regions=( "${regions[@]}" ${next//,/ } ); shift;;
-v|--verbose) VERBOSITY=$((${VERBOSITY}+1));;
--dry-run) dry_run=1;;
-m|--max-age)
case "$next" in
*h) max_age=$((${next%h}*60*60));;
*m) max_age=$((${next%m}*60));;
*d) max_age=$((${next%d}*60*60*24));;
*) max_age=$next;;
esac
[ $? -eq 0 ] || fail "failed to convert $next to a number"
[ "$(echo "${max_age}" | sed 's,[^0-9],,')" = "$max_age" ] ||
fail "$next was not a number i understood"
shift;;
--) shift; break;;
esac
shift;
done
[ $# -eq 0 ] || bad_Usage "unexpected arguments: $*"
[ -n "$max_age" ] ||
bad_Usage "must provide some reason to terminate. (--max-age?)"
TEMP_D=$(mktemp -d "${TMPDIR:-/tmp}/${0##*/}.XXXXXX") ||
fail "failed to make tempdir"
trap cleanup EXIT
my_start_time=$(date "+%s")
# program starts here
[ "${#regions[@]}" -eq 0 ] &&
regions=( $(ec2-list-all-regions) )
for reg in "${regions[@]}"; do
debug 2 "describe-instances --region=$reg"
xc2 describe-instances "--region=$reg" \
> "${TEMP_D}/$reg.instances.all" &
pids[${#pids[@]}]=$!
done
for((i=0;i<${#pids[@]};i++)); do
wait "${pids[$i]}" ||
fail "describe-instances in $reg failed"
done
# just make sure there are no empty fields (set -- doesn't respect them)
# now instead, a '.' indicates empty
sed -i -e 's,\t\t,\t.\t,g' -e 's,\t\t,\t.\t,g' "$TEMP_D/"*".instances.all"
OFMT="%-16s %-11s %-8s %s\n"
IFS="$TAB"
for reg in "${regions[@]}"; do
#INSTANCE i-9f351afc ami-a7cc07ce ec2-50-17-47-207.compute-1.amazonaws.com ip-10-32-151-165.ec2.internal running brickies 0 m1.small 2011-11-30T21:05:06+0000 us-east-1c aki-805ea7e9 monitoring-disabled 50.17.47.207 10.32.151.165 instance-store paravirtual xen sg-134da77a default
while read label iid ami host_ext host_int state key_name launch_index \
product_code size start az other; do
[ "$label" = "INSTANCE" ] || continue
# euca-describe-instances formats date as '2012-02-06T13:50:39.000Z'
# ec2-describe-instances formats date as '2012-02-06T20:30:19+0000'
cleaned_start=$(echo "${start}" | sed 's,T, ,; s,Z,0,; s,[+.], +,')
start_seconds=$(date --date "$cleaned_start" +"%s") ||
fail "failed to convert $start to seconds since epoch"
age=$(($my_start_time-$start_seconds))
if [ "$state" = "terminated" ]; then
op="dead"
elif [ $max_age -lt $age ]; then
op="kill"
printf "%s " "$iid " >> "$TEMP_D/$reg.to-kill"
else
op="pass"
fi
printf "$OFMT" "$reg" "$iid" "$op" "$age" >> "$TEMP_D/$reg.summary"
done < "$TEMP_D/$reg.instances.all"
done
IFS="$OIFS"
debugp 1 "$OFMT" "#region" "instance" "action" "age"
for reg in "${regions[@]}"; do
if [ ! -f "$TEMP_D/$reg.summary" ]; then
debugp 2 "$OFMT" "$reg" "i-0000000" "" ""
iids=""
else
cat "$TEMP_D/$reg.summary"
printf "\n" >> "${TEMP_D}/$reg.to-kill" && read iids < "${TEMP_D}/$reg.to-kill" ||
fail "failed to read iids from file for $reg (iids=$iids)"
fi
[ -n "$iids" ] || continue
[ $dry_run -eq 0 ] || continue
xc2 terminate-instances $iids >> "${TEMP_D}/term.out" ||
fail "failed to terminate instances $iids in $region"
done
exit 0
|