-
-
Save leucos/2c361f7d4767f8aea6dd to your computer and use it in GitHub Desktop.
| #!/bin/bash | |
| # | |
| # What is that | |
| # ============ | |
| # | |
| # This script will help you setting up your digital ocean | |
| # infrastructure with Ansible v2.0+ and DO API v2 | |
| # | |
| # Usually, when working with DO, one is supposed to use digital_ocean.py | |
| # inventory file, and spin up instances in a playbook. | |
| # However, this approach is very inconvenient for several reasons: | |
| # - it is veeeery slow | |
| # - your droplets won't have "ansible" names, just IPs | |
| # - ... consequently, putting them into groups is a pain (but doable) | |
| # - but if you succeed doing so, groups will only be available at run time | |
| # - you are required to have 'localhost' in your inventory, which basically ruins 'all' group purpose | |
| # - etc... | |
| # All in all, it is barely usable. | |
| # | |
| # This script attempts to make things easier. It works the following way: | |
| # | |
| # - it will read 'hosts' file in an inventory directory (passed as the first argument) | |
| # - it will spin up a DO droplet for each of these hosts (in parallel !) using ansible | |
| # (note that you can specify image type, droplet size, etc... in the inventory itself) | |
| # - it will generate an complementary inventory file (in the inventory | |
| # directory) containing droplets names along with their IP addresses, so you | |
| # won't need hitting the DO API anymore when running ansible. | |
| # Using this script, you can work like you use to do with bare metal machines, | |
| # without the chicken and egg problem of network configuration) | |
| # The script itself can: | |
| # - spin up droplets (`do_boot2.sh inventory_directory_path`) | |
| # - destroy droplets (`do_boot2.sh inventory_directory_path deleted`) | |
| # Since the droplets are created in parallel, you will also save tons of time | |
| # when building an infrastructure involving a bunch of droplets. As an example, | |
| # creating 8 droplets takes 130 seconds using this script, almost 570 using a | |
| # "classic approach". Destroying them takes 10 secs with this script, 55 using | |
| # a classic approach. So in the end, you get a 5 fold speed up, and a much | |
| # better usability. Of course, the more droplets, the more gain. | |
| # | |
| # DO_API_TOKEN environment variable must be set. | |
| # | |
| # Change defaults below | |
| # --------------------- | |
| # Digital Ocean default values | |
| # You can override them using do_something in your inventory file | |
| # Example: | |
| # | |
| # [www] | |
| # www1 do_size_slug="1gb" do_region_slug="nyc1" do_image=12345 | |
| # ... | |
| # | |
| # If you don't override in your inventory, the defaults below will apply | |
| DEFAULT_SIZE="512mb" # 512mb (override with do_size_slug) | |
| DEFAULT_REGION="ams2" # ams2 (override with do_region_slug) | |
| DEFAULT_IMAGE="ubuntu-14-04-x64" # Ubuntu 14.04 x64 (override with do_image_slug) | |
| DEFAULT_KEY=785648 # SSH key, change this ! (override with do_key) | |
| # localhost entry for temporary inventory | |
| # This is a temp inventory generated to start the DO droplets | |
| # You might want to change ansible_python_interpreter | |
| LOCALHOST_ENTRY="localhost ansible_python_interpreter=/usr/bin/python2" | |
| # Set state to present by default | |
| STATE=${2:-"present"} | |
| # digital_ocean module command to use | |
| # name, size, region, image and key will be filled automatically | |
| COMMAND="state=$STATE command=droplet private_networking=yes unique_name=yes" | |
| # --------------------- | |
| function bail_out { | |
| echo -e "\033[0;31m" | |
| echo $1 | |
| echo -e "\033[0m" | |
| echo -e "Usage: $0 <inventory_directory> [present|deleted]\n" | |
| echo -e "\tinventory_directory: the directory containing the inventory goal (compulsory)" | |
| echo -e "\tpresent: the droplet will be created if it doesn't exist (default)" | |
| echo -e "\tdeleted: the droplet will be destroyed if it exists\n" | |
| exit 1 | |
| } | |
| # Check that inventory is a directory | |
| # We need this since we generate a complementary inventory with IP addresses for hosts | |
| INVENTORY=$1 | |
| [[ ! -d "$INVENTORY" ]] && bail_out "Inventory does not exist, is not a directory, or is not set" | |
| [[ -z "$DO_API_TOKEN" ]] && bail_out "DO_API_TOKEN not set. Please visit https://cloud.digitalocean.com/settings/applications" | |
| JQ=`which jq` || bail_out "Unable to find required binary 'jq'. Please install it first (http://stedolan.github.io/jq/)" | |
| # Get a list of hosts from inventory dir | |
| HOSTS=$(ansible -i $1 --list-hosts all | awk '{ print $1 }' | tr '\n' ' ') | |
| # Clean up previously generated inventory | |
| rm ${INVENTORY}/generated > /dev/null 2>&1 | |
| # Creating temporary inventory with only localhost in it | |
| TEMP_INVENTORY=$(mktemp) | |
| echo Creating temporary inventory in ${TEMP_INVENTORY} | |
| echo ${LOCALHOST} > ${TEMP_INVENTORY} | |
| # Create droplets in // | |
| for i in ${HOSTS}; do | |
| SIZE=$(grep $i $1/hosts | grep do_size_slug | sed -e 's/.*do_size_slug=\(\d*\)/\1/') | |
| REGION=$(grep $i $1/hosts | grep do_region_slug | sed -e 's/.*do_region_slug=\(\d*\)/\1/') | |
| IMAGE=$(grep $i $1/hosts | grep do_image_slug | sed -e 's/.*do_image_slug=\(\d*\)/\1/') | |
| KEY=$(grep $i $1/hosts | grep do_key | sed -e 's/.*do_key=\(\d*\)/\1/') | |
| SIZE=${SIZE:-$DEFAULT_SIZE} | |
| REGION=${REGION:-$DEFAULT_REGION} | |
| IMAGE=${IMAGE:-$DEFAULT_IMAGE} | |
| KEY=${KEY:-$DEFAULT_KEY} | |
| if [ "${STATE}" == "present" ]; then | |
| echo "Creating $i of size $SIZE using image $IMAGE in region $REGION with key $KEY" | |
| else | |
| echo "Deleting $i" | |
| fi | |
| # echo " => $COMMAND name=$i size_id=$SIZE image_id=$IMAGE region_id=$REGION ssh_key_ids=$KEY" | |
| ansible localhost -c local -i ${TEMP_INVENTORY} -m digital_ocean \ | |
| -a "$COMMAND name=$i size_id=$SIZE image_id=$IMAGE region_id=$REGION ssh_key_ids=$KEY" & | |
| done | |
| wait | |
| # Now do it again to fill up complementary inventory | |
| if [ "${STATE}" == "present" ]; then | |
| for i in ${HOSTS}; do | |
| echo Checking droplet $i | |
| IP=$(ansible localhost -c local -i $TEMP_INVENTORY -m digital_ocean -a "state=present command=droplet unique_name=yes name=$i" | sed -e 's/localhost | success >> //' | $JQ '.droplet.networks.v4[] | select(.type == "public") | .ip_address' | cut -f2 -d'"') | |
| echo "$i ansible_ssh_host=$IP" >> ${INVENTORY}/generated | |
| done | |
| fi | |
| echo "All done !" |
seems like the latest ansible update has broken the last part of this script, it doesn't createthe "generated" file any more
parse error: Invalid numeric literal at line 1, column 10
All done !
if you reading these comments, I have fixed the problem in my fork
Thank you jtktam!
parsing of the override is incorrect
With latest Versions of dopy and ansible the parameter api_token is missing. Fixed in my fork. ;)
I have to say the blog post was right on, and this script was exactly what I was looking for and couldn't find. Generating a host file is much more compatible with my existing playbooks. I thank you. Also you fork-ers, you helped too.
Cheers,
Arron
Ah yeah, there it is. The output of --list-hosts all has changed to:
hosts (3):
host1
host2
host3
So awk output ends up being:
hosts host1 host2 host3
Change Line 94 of jtktams fork to:
HOSTS=$(ansible -i $1 --list-hosts all | awk '{ print $1 }' | awk '{if (NR!=1) {print}}' | tr '\n' ' ')
Additionally the parsing does not work if there are more than one override:
Config example:
host1 do_region_slug="nyc1" do_size_slug="512mb"
Current line example:
SIZE=$(grep $i $1/hosts | grep do_size_slug | sed -e 's/.*do_size_slug=\(\d*\)/\1/'
Resulting value of $SIZE:
"nyc1" do_size_slug="512mb"
Need to remove everything in the line after the second "
This is fixed in my fork.
Working lines 106-109
SIZE=$(grep $i $1/hosts | grep do_size_slug | sed -e 's/.*do_size_slug=\(\d*\)/\1/' | sed -r 's/(([^"]*"){2}).*/\1/')
REGION=$(grep $i $1/hosts | grep do_region_slug | sed -e 's/.*do_region_slug=\(\d*\)/\1/' | sed -r 's/(([^"]*"){2}).*/\1/')
IMAGE=$(grep $i $1/hosts | grep do_image_slug | sed -e 's/.*do_image_slug=\(\d*\)/\1/' | sed -r 's/(([^"]*"){2}).*/\1/')
KEY=$(grep $i $1/hosts | grep do_key | sed -e 's/.*do_key=\(\d*\)/\1/' | sed -r 's/(([^"]*"){2}).*/\1/')
LOCALHOST_ENTRY="localhost ansible_python_interpreter=/usr/bin/python2"
This variable is not used at line 102
echo ${LOCALHOST} > ${TEMP_INVENTORY}
should be
echo ${LOCALHOST_ENTRY} > ${TEMP_INVENTORY}
DEFAULT_KEY is the Digital Ocean ssh key Fingerprint. That took me a few minutes to figure out.
It can be generated as seen on Digital Ocean with the following command:
ssh-keygen -lv -E md5 -f ./id_rsa.pub
hi. i am trying to figure out how to best use your script. I got it running and it output a file called "generated"
how do i use this file in my ansible?
thanks