Skip to content

Instantly share code, notes, and snippets.

@lifning
Created February 15, 2013 19:48
Show Gist options
  • Save lifning/4962936 to your computer and use it in GitHub Desktop.
Save lifning/4962936 to your computer and use it in GitHub Desktop.
A backup script I wrote as a system administration course assignment in 2011.
#!/bin/bash
set -e
# print usage information if -h flag is passed
usage()
{
cat 1>&2 <<EOF
usage: $0 [-hv] [-i instance] [-m method] [-t tag] -d dir
-h Print a usage statement and exit.
-v Be verbose.
-e recipient Encrypt the data for the specified recipient.
-i instance Attach the volume in question to the given instance.
-m (dd|rsync) Use the given method to perform the backup.
-t tag Use the volume with the given tag.
EOF
}
VERBOSE=0
ENCRYPT=cat
INSTANCE=
TERMINATE=0
# default value of METHOD for when -m isn't specified:
METHOD=dd
# default value of TAG for when -t isn't specified:
TAG=ec2-backup
DIRECTORY=
# parse command line options using getopts
while getopts "hve:i:m:t:d:" OPTION
do
case $OPTION in
h) usage ; exit ;;
v) VERBOSE=1 ;;
e)
if [ "${ENCRYPT}" = "cat" ] ; then
ENCRYPT="gpg -e"
fi
ENCRYPT="$ENCRYPT -r $OPTARG" ;;
i) INSTANCE=$OPTARG ;;
m)
case $OPTARG in
dd) METHOD=dd ;;
rsync) METHOD=rsync ;;
*) # exit with an error code if an invalid method is given
echo $0: "invalid -m value (should be dd or rsync)" 1>&2
exit 2
;;
esac
;;
t) TAG=$OPTARG ;;
d)
if [ -d $OPTARG ] ; then
DIRECTORY=$OPTARG
else
# exit with an error code if argument to -d is not a directory
echo $0: $OPTARG" is not a valid directory" 1>&2
exit 3
fi
;;
# show usage and exit with an error code if invalid arguments given
# note: getopts already shows a helpful message before "usage" is called
*) usage ; exit 1 ;;
esac
done
# don't continue if a directory wasn't specified!
if [ x"${DIRECTORY}" = x"" ] ; then
echo $0: "a directory must be given with '-d directory'" 1>&2
exit 4
fi
# handling verbosity in a somewhat clever way
VERBDIRECT=true
if [ $VERBOSE = 1 ] ; then
VERBDIRECT=cat
fi
# some encryption-related details
if [ "${ENCRYPT}" != "cat" ] ; then
# don't continue if trying to use gpg and rsync together!
if [ $METHOD != dd ] ; then
echo $0: "must use method 'dd' when using PGP encryption"
exit 10
else
echo "encrypting using command: $ENCRYPT"|$VERBDIRECT
fi
fi
# get size of directory in GB
SIZE=$(du -sBGB $DIRECTORY | awk '{print $1}' | sed 's/[^0-9]//g')
# add 1GiB of size to "round up" / for overhead
SIZE=$(expr $SIZE + 1)
# availability zone (needs to be same between instance and volume)
ZONE=
if [ x"${INSTANCE}" != x"" ] ; then
ZONE=$(ec2din | grep "^INSTANCE.$INSTANCE.*running" | awk '{print $11}')
fi
if [ x"${ZONE}" = x"" ] ; then
ZONE=$(ec2-describe-availability-zones | head -1 | awk '{print $2}')
fi
# get information about existing volume
OUTPUT=$(ec2dvol -F "tag-key=$TAG" | grep '^VOLUME.*available' | head -1)
VOLUME=$(echo $OUTPUT | awk '{print $2}')
VOLSIZE=$(echo $OUTPUT | awk '{print $3}')
VOL_JUST_CREATED=0
# does volume exist? if not, create volume
if [ x"${VOLUME}" = x"" ] ; then
[ $VERBOSE = 1 ] && echo "creating volume in $ZONE"
# the value for -z is a hack, don't know how to make it not required...
VOLUME=$(ec2addvol -s $SIZE -z $ZONE | awk '{print $2}')
if [ $? != 0 ] ; then
echo $0: "couldn't create volume." 1>&2
exit 5
fi
# tag the new volume, or die trying
ec2tag -t $TAG $VOLUME |$VERBDIRECT || exit 6
VOL_JUST_CREATED=1
# if the existing volume isn't big enough, throw an error!
elif [ "$VOLSIZE" -lt "$SIZE" ] ; then
echo $0: "specified volume is too small! $VOLSIZE < $SIZE" 1>&2
exit 7
else
[ $VERBOSE = 1 ] && echo "using $VOLUME"
fi
# if no instance specified at command line, start instance
if [ x"${INSTANCE}" = x"" ] ; then
EC2_RUN_INSTANCES_FLAGS=$EC2_RUN_INSTANCES_FLAGS" -z $ZONE"
OUTPUT=$(ec2-start-instance)
if [ $? != 0 ] ; then
echo $0: "couldn't create instance to attach volume." 1>&2
exit 8
fi
INSTANCE=$(echo $OUTPUT | awk '{print $1}')
HOSTNAME=$(echo $OUTPUT | awk '{print $2}')
TERMINATE=1
# hackish! give the machine some time to start up services, especially ssh
sleep 20
else
# otherwise, just get its host name
HOSTNAME=$(ec2din|grep "^INSTANCE.$INSTANCE.*running"|awk '{print $4}')
if [ x"${HOSTNAME}" = x"" ] ; then
echo $0: "no such instance!" 1>&2
exit 9
fi
fi
# attach volume to instance, or die trying
ec2-attach-volume -i $INSTANCE -d /dev/sdh $VOLUME |$VERBDIRECT || exit 10
# give it some time to attach...
sleep 20
# do the backup by whatever method was specified
if [ $METHOD = rsync ] ; then
# find out if we need to format
if [ $VOL_JUST_CREATED = 1 ] ; then
ssh $HOSTNAME "yes | mkfs /dev/sdh" 2>&1 |$VERBDIRECT
fi
# mount the drive, sync the files, and unmount
ssh $HOSTNAME "mkdir -p /mnt/ec2backup" 2>&1 |$VERBDIRECT
ssh $HOSTNAME "mount /dev/sdh /mnt/ec2backup" 2>&1 |$VERBDIRECT
rsync -aq $DIRECTORY $HOSTNAME:/mnt/ec2backup 2>&1 |$VERBDIRECT
ssh $HOSTNAME "umount /dev/sdh" 2>&1 |$VERBDIRECT
else
tar c $DIRECTORY | $ENCRYPT | ssh $HOSTNAME "dd of=/dev/sdh" 2>&1 |$VERBDIRECT
fi
ec2-detach-volume $VOLUME |$VERBDIRECT
# terminate instance if we started it in this script
if [ $TERMINATE = 1 ] ; then
ec2-terminate-instances $INSTANCE |$VERBDIRECT
fi
#!/bin/bash
# print usage information if -h flag is passed
usage()
{
cat 1>&2 <<EOF
usage: $0 [-hv] [-a ami]
-a AMI Start an instance of the given AMI.
-h Print a usage statement and exit.
-v Be verbose.
EOF
}
# default value of AMI for when -a isn't specified:
AMI=ami-b232d0db
VERBOSE=0
# parse command line options using getopts
while getopts "hva:" OPTION
do
case $OPTION in
h) usage ; exit ;;
v) VERBOSE=1 ;;
a) AMI=$OPTARG ;;
# show usage and exit with an error code if invalid arguments given
# note: getopts already shows a helpful message before "usage" is called
*) usage ; exit 1 ;;
esac
done
# for the sake of not breaking 80 characters in width, rename this var...
EC2_RIF=$EC2_RUN_INSTANCES_FLAGS
# start the instance and get the instance ID
INSTANCE=$(ec2run $AMI $EC2_RIF | grep '^INSTANCE' | awk '{print $2}')
if [ .$INSTANCE != . ] ; then
# the first part of the output, the instance id. don't output a newline yet.
echo -n "$INSTANCE "
# check the status of the machine until it is running and has a host
STATUS=pending
while [ .$STATUS == .pending ] ; do
STATUS=$(ec2din | grep "^INSTANCE.$INSTANCE" | awk '{print $4}')
[ $VERBOSE == 1 ] && echo "waiting for instance to be ready..." 1>&2
sleep 5
done
# the fourth thing in the output of ec2din, which was its status before,
# just happens to be the position of the hostname! lucky us.
echo $STATUS
else
echo $0: "an error occurred while starting the instance." 1>&2
exit 2
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment