#! /bin/sh
### BEGIN INIT INFO
# Provides: ec2setup
# Required-Start: $local_fs $remote_fs $network $syslog
# Required-Stop: $local_fs $remote_fs
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Set up EC2 configuration.
# Description: Set up EC2 configuration.
### END INIT INFO
# /etc/rc.d/rc.ec2setup: Set up EC2 AWS configuration.
# Copyright (C) 2012-2014 Sergey Poznyakoff
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
confdir=/etc/default
cnameslist=/var/run/ec2cnames
CONFFILE=$confdir/ec2setup
if test -r $CONFFILE; then
. $CONFFILE
fi
id=$(curl -s http://169.254.169.254/latest/meta-data/instance-id)
if test -n "$EC2_REGION"; then
ec2_options="--region $EC2_REGION"
else
ec2_options=
fi
if test -n "$EC2_ECLAT_OPTIONS"; then
ec2_options="$ec2_options $EC2_ECLAT_OPTIONS"
fi
# Get all tags into shell variables
echo "$0: Getting tags..."
eval $(eclat $ec2_options --format-expr '
if (.DescribeTagsResponse) {
for (var in .DescribeTagsResponse.tagSet.item)
print("tag_", var.key,"=",var.value,"\n");
} else if (.Response.Errors) {
error("Error: ",.Response.Errors.Error.Message,"\n");
exit(1);
} else {
error("Unrecognized response:\n");
dump(.);
exit(1);
}' describe-tags resource-id=$id) || exit 2
# ec2_get_tag TAG
ec2_get_tag() {
local tag=tag_$1
eval val=\$$tag
echo $val
}
# Get info about this host. Sets the variables:
# hostname
# ip
# descr
# commonname
# If an argument is supplied, it supplies the status code to return to
# the shell when unable to determine the hostname.
gethostinfo() {
test -z "$ip" &&
ip=$(curl -s http://169.254.169.254/latest/meta-data/public-ipv4)
if test -z "$ip"; then
echo >&2 "$0: cannot get IP for $id"
return
fi
if test -n "$EC2_SETUP_TAG_HOSTNAME"; then
hostname=$(ec2_get_tag $EC2_SETUP_TAG_HOSTNAME)
fi
if test -z "$hostname"; then
echo >&2 "$0: no hostname (tag $EC2_SETUP_TAG_HOSTNAME) set for $id"
test -n "$1" && exit $1
return
fi
commonname=$(echo "$hostname" | sed 's/,/%2C/g')
descr=$(ec2_get_tag $EC2_SETUP_TAG_DESCR)
}
# update_hosts IP FQDN
update_hosts() {
echo "$0: Updating /etc/hosts"
tempfile=/var/run/etchosts.$$
sed '/#:# This file/,/#:# End/d' /etc/hosts > $tempfile
cat >/etc/hosts <> /etc/hosts
rm $tempfile
}
# set_hostname IP FQDN
# Set this hostname. Update /etc/hosts accordingly
set_hostname() {
echo "$0: Setting hostname"
hostname $2
echo $2 > /etc/hostname
update_hosts $1 $2
}
# ip_assoc IP
ip_assoc() {
allocid=$(eclat $ec2_options --format-expr='
if (.DescribeAddressesResponse.addressesSet.item.allocationId)
print(.DescribeAddressesResponse.addressesSet.item.allocationId,"\n");
' describe-addresses $1)
if [ -z "$allocid" ]; then
echo "$0: Attaching IP address $ip..."
eclat $ec2_options associate-address $id $1
else
echo "$0: Attaching IP address $ip from allocation ID $allocid"
eclat $ec2_options associate-address --vpc $id $allocid
fi
}
ip_status() {
eclat $ec2_options describe-addresses instance-id=$id
ip=$(curl -s http://169.254.169.254/latest/meta-data/public-ipv4)
echo "Assigned address $ip"
}
memsize() {
free|sed -r -n 's/Mem:[[:space:]]+([0-9]+).*/\1/p'
}
numcpus() {
egrep -c '^processor[[:space:]]+:' /proc/cpuinfo
}
distro_id() {
if [ -f /etc/os-release ]; then
( . /etc/os-release
echo "grayOSDistribution: $ID"
echo "grayOSDistributionVersion: $VERSION"
echo "grayOSDistributionFamily: $ID_LIKE" )
elif [ -f /etc/lsb-release ]; then
( . /etc/lsb-release
DISTRIB_ID=`echo $DISTRIB_ID|tr A-Z a-z`
case $DISTRIB_ID in
ubuntu|debian) DISTRIB_FAMILY=debian;;
centos|rhel|fedora)
DISTRIB_FAMILY=rhel;;
*) DISTRIB_FAMILY=$DISTRIB_ID
esac
echo "grayOSDistribution: $DISTRIB_ID"
echo "grayOSDistributionVersion: $DISTRIB_RELEASE"
echo "grayOSDistributionFamily: $DISTRIB_FAMILY" )
fi
# FIXME: More variants: /etc/redhat-release, etc. See initreg
}
# uses globals: $hostname, $descr, $commonname
machine_up() {
local cn=$commonname
if test -n "$EC2_SETUP_LDAP_BASE"; then
dn="cn=$cn,$EC2_SETUP_LDAP_BASE"
echo "$0: Creating LDAP entry for $dn"
eval ldapdelete $EC2_SETUP_LDAP_OPTIONS "$dn" || true
devmap=$(eclat $ec2_options --format-expr='
if (.DescribeVolumesResponse.volumeSet.item) {
for (var in .DescribeVolumesResponse.volumeSet.item) {
if (var.attachmentSet.item) {
let att = var.attachmentSet.item;
print("print("e;grayDeviceMapping: ", att.device, "=", att.volumeId, " ", var.size, "G");
if (var.tagSet.item.value)
print(" (", var.tagSet.item.value, ")");
print("\\n"e;);\n");
}
}
}' describe-volumes attachment.instance-id=$id | sed 's/"//g;s/"e;/"/g')
eclat $ec2_options --format-expr='
if (.DescribeInstancesResponse.reservationSet.item.instancesSet.item) {
let inst = .DescribeInstancesResponse.reservationSet.item.instancesSet.item;
print("dn: '"$dn"'\n");
print("cn: '"$cn"'\n");
print("uid: '"$cn"'\n");
print("objectClass: grayMachine\n");
print("objectClass: account\n");
print("grayStatus: up\n");
print("grayInstanceID: ", inst.instanceId, "\n");
print("grayInstanceType: ", inst.instanceType, "\n");
print("grayInstanceRegion: ", inst.placement.availabilityZone, "\n");
'"$devmap"'
} else if (.Response.Errors)
error("Error: ",.Response.Errors.Error.Message,"\n");
else {
error("Unrecognized response:\n");
dump(.);
}' describe-instances instance-id=$id |
(cat -
echo "grayHostName: $hostname"
echo "ipHostNumber: $ip"
echo "grayRAMSize: " `memsize`
echo "grayCPUCount: " `numcpus`
echo "grayOSName: " `uname -s`
echo "grayOSVersion: " `uname -r`
echo "grayOSArchitecture: " `uname -m`
distro_id
test -n "$descr" && echo "description: $descr"
test -n "$EC2_SETUP_STATIC_INFO" && echo "$EC2_SETUP_STATIC_INFO") |
eval ldapadd $EC2_SETUP_LDAP_OPTIONS
fi
}
# uses globals: $hostname, $commonname
machine_down() {
if test -n "$EC2_SETUP_LDAP_BASE"; then
dn="cn=$commonname,$EC2_SETUP_LDAP_BASE"
echo "$0: Updating LDAP entry for $dn"
eval ldapmodify $EC2_SETUP_LDAP_OPTIONS </dev/null
else
nsupdate -k $1 2>&1 |
grep -v "update failed: NXRRSET"
fi
}
makesig() {
echo $id | base64
}
# Return true if $1 can be registerd in the DNS.
# Uses globals: $id, $commonname
# FIXME: Remove the last test when the transition period is over
nsfilter() {
local s
s=$(/usr/bin/host -tTXT _sig.$1 | \
sed -n '/.* descriptive text *"/{s///;s/"$//p}')
test -z "$s" || test "$s" = "$(makesig)" || test "$s" = "$id"
}
get_nsupdate_value() {
local value="$1" domain="$2" ret
set -- $value
if [ $# -eq 1 ]; then
if ! echo "$value" | grep -q '^='; then
set -- ".=$value"
fi
fi
if ! echo "$domain" | grep -q '\.$'; then
domain="$domain."
fi
for i
do
x=$domain
while :
do
case $i in
${x:-.}=*)
echo ${i##${x:-.}=}
return;;
esac
if [ -z "$x" ]; then
break
fi
x=${x#*.}
done
done
}
# register_hostname
# Uses globals: $hostname, $ip, $descr, $id
register_hostname() {
local localzone
echo "$0: registering hostname $hostname, ip $ip"
if test -n "$EC2_SETUP_NSUPDATE_SERVER" &&
test -n "$EC2_SETUP_NSUPDATE_KEY"; then
if ! nsfilter "$hostname"; then
echo >&2 "$0: cannot update A record for $hostname: signature mismatch"
return
fi
server=$(get_nsupdate_value "$EC2_SETUP_NSUPDATE_SERVER" "$hostname")
key=$(get_nsupdate_value "$EC2_SETUP_NSUPDATE_KEY" "$hostname")
if test -n "$server" && test -n "$key"; then
echo "$0: Updating DNS A records on $server (key $key)"
(cat <&2 "$0: cannot update A record for $hostname: signature mismatch"
return
fi
server=$(get_nsupdate_value "$EC2_SETUP_NSUPDATE_SERVER" "$hostname")
key=$(get_nsupdate_value "$EC2_SETUP_NSUPDATE_KEY" "$hostname")
if test -n "$server" && test -n "$key"; then
(cat < $cnameslist
cnames=
if test -n "$EC2_SETUP_TAG_CNAMES"; then
cnames=$(ec2_get_tag $EC2_SETUP_TAG_CNAMES | tr ' ' '\n' | sed "
/^$/d
/.*\.$/b
/.*/s/.*/&.$hostname/p")
fi
cnames="$cnames $EC2_SETUP_STATIC_CNAMES "$(get_vhost_cnames)
echo "$cnames" | tr -s ' ' | tr ' ' '\n' | sed '/^$/d' |
sort | uniq > $cnameslist
if test -s $cnamelist; then
while read cname
do
if test -z "$cname"; then
continue
fi
if ! nsfilter "$cname"; then
echo >&2 "$0: cannot update CNAME record for $hostname: signature mismatch"
continue
fi
server=$(get_nsupdate_value "$EC2_SETUP_NSUPDATE_SERVER" "$cname")
key=$(get_nsupdate_value "$EC2_SETUP_NSUPDATE_KEY" "$cname")
if test -n "$server" && test -n "$key"; then
(cat <&2 "$0: cannot update CNAME record for $hostname: signature mismatch"
continue
fi
server=$(get_nsupdate_value "$EC2_SETUP_NSUPDATE_SERVER" "$cname")
key=$(get_nsupdate_value "$EC2_SETUP_NSUPDATE_KEY" "$cname")
if test -n "$server" && test -n "$key"; then
(cat <&2 "$0: no IP associated with $id"
fi
fi
gethostinfo
set_hostname $ip $hostname
machine_up
register_hostname
register_cnames
}
update_ldap() {
ip=
if test -n "$EC2_SETUP_TAG_IP"; then
ip=$(ec2_get_tag $EC2_SETUP_TAG_IP)
fi
gethostinfo
machine_up
}
status() {
ip_status
}
stop() {
hostname=$(hostname)
commonname=$(echo "$hostname" | sed 's/,/%2C/g')
machine_down
deregister_hostname
deregister_cnames
}
update_cnames() {
gethostinfo 1
cnames=$(get_vhost_cnames)
if test -r $cnameslist; then
if test -n "$cnames" &&
echo "$cnames" | cmp - $cnameslist >/dev/null; then
return
fi
else
if test -z "$cnames"; then
return
fi
fi
register_cnames
}
if test $# -eq 0; then
if test -n "$DIREVENT_FILE"; then
case $(pwd) in
/etc/apache2/sites-available)
for file in /etc/apache2/sites-enabled/*
do
if test -h $file; then
s=$(readlink -f $file)
if test "$s" = /etc/apache2/sites-available/$DIREVENT_FILE
then
update_cnames
break
fi
fi
done
;;
/etc/apache2/sites-enabled)
update_cnames
esac
exit 0
fi
fi
case $1 in
start) start;;
status) status;;
stop) stop;;
updatedb)
update_ldap;;
hostname)
gethostinfo 1
register_hostname
;;
clear-hostname)
gethostinfo 1
deregister_hostname
;;
cnames) gethostinfo 1
register_cnames
;;
clear-cnames)
gethostinfo 1
deregister_cnames;;
restart|force-reload)
echo >&2 "$0: ignoring $1";;
*) cat >&2 <