Newer
Older
ansible_spark_openstack / openstack_inventory.py
Johan Dahlberg on 4 Mar 2015 4 KB Initial commit
#!/usr/bin/env python
################################################################################
# Dynamic inventory generation for Ansible
# In orignal version found here: https://github.com/lukaspustina/dynamic-inventory-for-ansible-with-openstack
# Author lukas.pustina@codecentric.de
# With additions from Johan Dahlberg - johan.dahlberg@medsci.uu.se
#
# This Python script generates a dynamic inventory based on OpenStack instances.
#
# The script is passed via -i <script name> to ansible-playbook. Ansible
# recognizes the execute bit of the file and executes the script. It then
# queries nova via the novaclient module and credentials passed via environment
# variables -- see below.
#
# The script iterates over all instances of the given tenant and checks if the
# instances' metadata have set keys OS_METADATA_KEY -- see below. These keys shall
# contain all Ansible host groups comma separated an instance shall be part of,
# e.g., u'ansible_host_groups': u'admin_v_infrastructure,apt_repos'.
# It is also possible to set Ansible host variables, e.g.,
# u'ansible_host_vars': u'dns_server_for_domains->domain1,domain2;key2->value2'
# Values with a comma will be transformed into a list.
#
# Metadata of an instance may be set during boot, e.g.,
# > nova boot --meta <key=value>
# , or to a running instance, e.g.,
# nova meta <instance name> set <key=value>
#
# *** Requirements ***
# * Python: novaclient module be installed which is part of the nova ubuntu
# package.
# * The environment variables OS_USERNAME, OS_PASSWORD, OS_TENANT_NAME,
# OS_AUTH_URL and OS_NETWORK_NAME must be set according to nova.
#
################################################################################

from __future__ import print_function
from novaclient.v2 import client
import os, sys, json

OS_METADATA_KEY = {
	'host_groups': 'ansible_host_groups',
	'host_vars': 'ansible_host_vars'
}

try:
	OS_NETWORK_NAME = os.environ['OS_NETWORK_NAME']
except KeyError as e:
	print("ERROR: environment variable %s is not defined" % e, file=sys.stderr)
	sys.exit(-1)

def main(args):
	credentials = getOsCredentialsFromEnvironment()
	nt = client.Client(credentials['USERNAME'], credentials['PASSWORD'], credentials['TENANT_NAME'], credentials['AUTH_URL'], service_type="compute")

	inventory = {}
	inventory['_meta'] = { 'hostvars': {} }

	for server in nt.servers.list():
		floatingIp = getFloatingIpFromServerForNetwork(server, OS_NETWORK_NAME)
		if floatingIp:
			for group in getAnsibleHostGroupsFromServer(nt, server.id):
				addServerToHostGroup(group, floatingIp, inventory)
			host_vars = getAnsibleHostVarsFromServer(nt, server.id)
			if host_vars:
				addServerHostVarsToHostVars(host_vars, floatingIp, inventory)

	dumpInventoryAsJson(inventory)

def getOsCredentialsFromEnvironment():
	credentials = {}
	try:
		credentials['USERNAME'] = os.environ['OS_USERNAME']
		credentials['PASSWORD'] = os.environ['OS_PASSWORD']
		credentials['TENANT_NAME'] = os.environ['OS_TENANT_NAME']
		credentials['AUTH_URL'] = os.environ['OS_AUTH_URL']
	except KeyError as e:
		print("ERROR: environment variable %s is not defined" % e, file=sys.stderr)
		sys.exit(-1)

	return credentials

def getAnsibleHostGroupsFromServer(novaClient, serverId):
	metadata = getMetaDataFromServer(novaClient, serverId, OS_METADATA_KEY['host_groups'])
	if metadata:
		return metadata.split(',')
	else:
		return []

def getMetaDataFromServer(novaClient, serverId, key):
	return novaClient.servers.get(serverId).metadata.get(key, None)

def getAnsibleHostVarsFromServer(novaClient, serverId):
	metadata = getMetaDataFromServer(novaClient, serverId, OS_METADATA_KEY['host_vars'])
	if metadata:
		host_vars = {}
		for kv in metadata.split(';'):
			key, values = kv.split('->')
			values = values.split(',')
			host_vars[key] = values
		return host_vars
	else:
		return None

def getFloatingIpFromServerForNetwork(server, network):
	for addr in server.addresses.get(network):
		if addr.get('OS-EXT-IPS:type') == 'floating':
			return addr['addr']
                if addr.get('OS-EXT-IPS:type') == 'fixed':
                        return addr['addr']
	return None

def addServerToHostGroup(group, floatingIp, inventory):
	host_group = inventory.get(group, {})
	hosts = host_group.get('hosts', [])
	hosts.append(floatingIp)
	host_group['hosts'] = hosts
	inventory[group] = host_group

def addServerHostVarsToHostVars(host_vars, floatingIp, inventory):
	inventory_host_vars = inventory['_meta']['hostvars'].get(floatingIp, {})
	inventory_host_vars.update(host_vars)
	inventory['_meta']['hostvars'][floatingIp] = inventory_host_vars

def dumpInventoryAsJson(inventory):
	print(json.dumps(inventory, indent=4))


if __name__ == "__main__":
    main(sys.argv)