#!/usr/bin/env python2 # pylint: skip-file # (c) 2012, Marco Vito Moscaritolo # # This file is part of Ansible, # # Ansible 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 of the License, or # (at your option) any later version. # # Ansible 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 Ansible. If not, see . import sys import re import os import ConfigParser from novaclient import client as nova_client try: import json except ImportError: import simplejson as json ################################################### # executed with no parameters, return the list of # all groups and hosts NOVA_CONFIG_FILES = [os.path.join(os.path.dirname(os.path.realpath(__file__)), "nova.ini"), os.path.expanduser(os.environ.get('ANSIBLE_CONFIG', "~/nova.ini")), "/etc/ansible/nova.ini"] NOVA_DEFAULTS = { 'auth_system': None, 'region_name': None, 'service_type': 'compute', } def nova_load_config_file(): p = ConfigParser.SafeConfigParser(NOVA_DEFAULTS) for path in NOVA_CONFIG_FILES: if os.path.exists(path): p.read(path) return p return None def get_fallback(config, value, section="openstack"): """ Get value from config object and return the value or false """ try: return config.get(section, value) except ConfigParser.NoOptionError: return False def push(data, key, element): """ Assist in items to a dictionary of lists """ if (not element) or (not key): return if key in data: data[key].append(element) else: data[key] = [element] def to_safe(word): ''' Converts 'bad' characters in a string to underscores so they can be used as Ansible groups ''' return re.sub(r"[^A-Za-z0-9\-]", "_", word) def get_ips(server, access_ip=True): """ Returns a list of the server's IPs, or the preferred access IP """ private = [] public = [] address_list = [] # Iterate through each servers network(s), get addresses and get type addresses = getattr(server, 'addresses', {}) if len(addresses) > 0: for network in addresses.itervalues(): for address in network: if address.get('OS-EXT-IPS:type', False) == 'fixed': private.append(address['addr']) elif address.get('OS-EXT-IPS:type', False) == 'floating': public.append(address['addr']) if not access_ip: address_list.append(server.accessIPv4) address_list.extend(private) address_list.extend(public) return address_list access_ip = None # Append group to list if server.accessIPv4: access_ip = server.accessIPv4 if (not access_ip) and public and not (private and prefer_private): access_ip = public[0] if private and not access_ip: access_ip = private[0] return access_ip def get_metadata(server): """Returns dictionary of all host metadata""" get_ips(server, False) results = {} for key in vars(server): # Extract value value = getattr(server, key) # Generate sanitized key key = 'os_' + re.sub(r"[^A-Za-z0-9\-]", "_", key).lower() # Att value to instance result (exclude manager class) #TODO: maybe use value.__class__ or similar inside of key_name if key != 'os_manager': results[key] = value return results config = nova_load_config_file() if not config: sys.exit('Unable to find configfile in %s' % ', '.join(NOVA_CONFIG_FILES)) # Load up connections info based on config and then environment # variables username = (get_fallback(config, 'username') or os.environ.get('OS_USERNAME', None)) api_key = (get_fallback(config, 'api_key') or os.environ.get('OS_PASSWORD', None)) auth_url = (get_fallback(config, 'auth_url') or os.environ.get('OS_AUTH_URL', None)) project_id = (get_fallback(config, 'project_id') or os.environ.get('OS_TENANT_NAME', None)) region_name = (get_fallback(config, 'region_name') or os.environ.get('OS_REGION_NAME', None)) auth_system = (get_fallback(config, 'auth_system') or os.environ.get('OS_AUTH_SYSTEM', None)) # Determine what type of IP is preferred to return prefer_private = False try: prefer_private = config.getboolean('openstack', 'prefer_private') except ConfigParser.NoOptionError: pass client = nova_client.Client( version=config.get('openstack', 'version'), username=username, api_key=api_key, auth_url=auth_url, region_name=region_name, project_id=project_id, auth_system=auth_system, service_type=config.get('openstack', 'service_type'), ) # Default or added list option if (len(sys.argv) == 2 and sys.argv[1] == '--list') or len(sys.argv) == 1: groups = {'_meta': {'hostvars': {}}} # Cycle on servers for server in client.servers.list(): access_ip = get_ips(server) # Push to name group of 1 push(groups, server.name, access_ip) # Run through each metadata item and add instance to it for key, value in server.metadata.iteritems(): composed_key = to_safe('tag_{0}_{1}'.format(key, value)) push(groups, composed_key, access_ip) # Do special handling of group for backwards compat # inventory groups group = server.metadata['group'] if 'group' in server.metadata else 'undefined' push(groups, group, access_ip) # Add vars to _meta key for performance optimization in # Ansible 1.3+ groups['_meta']['hostvars'][access_ip] = get_metadata(server) # Return server list print(json.dumps(groups, sort_keys=True, indent=2)) sys.exit(0) ##################################################### # executed with a hostname as a parameter, return the # variables for that host elif len(sys.argv) == 3 and (sys.argv[1] == '--host'): results = {} ips = [] for server in client.servers.list(): if sys.argv[2] in (get_ips(server) or []): results = get_metadata(server) print(json.dumps(results, sort_keys=True, indent=2)) sys.exit(0) else: print "usage: --list ..OR.. --host " sys.exit(1)