Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improvements to OpenStack inventory script #7444

Merged
merged 4 commits into from
Apr 14, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
29 changes: 22 additions & 7 deletions plugins/inventory/nova.ini
Original file line number Diff line number Diff line change
@@ -1,30 +1,45 @@
# Ansible OpenStack external inventory script

[openstack]

#-------------------------------------------------------------------------
# Required settings
#-------------------------------------------------------------------------

# API version
version = 2

# OpenStack nova username
username =

# OpenStack nova api_key
# OpenStack nova api_key or password
api_key =

# OpenStack nova auth_url
auth_url =

# Authentication system
auth_system =
# OpenStack nova project_id or tenant name
project_id =

#-------------------------------------------------------------------------
# Optional settings
#-------------------------------------------------------------------------

# OpenStack nova project_id
project_id =
# Authentication system
# auth_system = keystone

# Serverarm region name to use
region_name =
# region_name =

# Specify a preference for public or private IPs (public is default)
# prefer_private = False

# What service type (required for newer nova client)
# service_type = compute


# TODO: Some other options
# insecure =
# endpoint_type =
# extensions =
# service_type =
# service_name =
194 changes: 142 additions & 52 deletions plugins/inventory/nova.py
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,9 @@

try:
import json
except:
except ImportError:
import simplejson as json

from ansible.module_utils.openstack import *

###################################################
# executed with no parameters, return the list of
# all groups and hosts
Expand All @@ -41,6 +39,7 @@
NOVA_DEFAULTS = {
'auth_system': None,
'region_name': None,
'service_type': 'compute',
}


Expand All @@ -54,45 +53,152 @@ def nova_load_config_file():

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(
config.get('openstack', 'version'),
config.get('openstack', 'username'),
config.get('openstack', 'api_key'),
config.get('openstack', 'project_id'),
config.get('openstack', 'auth_url'),
region_name = config.get('openstack', 'region_name'),
auth_system = config.get('openstack', 'auth_system')
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'),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I tested your PR and adding service_type and not creating a default for it causes it to fail with an older .ini file that doesn't specify service_type. I submitted the following PR to fix this issue: carsongee#1

)

if len(sys.argv) == 2 and (sys.argv[1] == '--list'):
groups = {}

# 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():
private = openstack_find_nova_addresses(getattr(server, 'addresses'), 'fixed', 'private')
public = openstack_find_nova_addresses(getattr(server, 'addresses'), 'floating', 'public')

# Define group (or set to empty string)
group = server.metadata['group'] if server.metadata.has_key('group') else 'undefined'

# Create group if not exist
if group not in groups:
groups[group] = []

# Append group to list
if server.accessIPv4:
groups[group].append(server.accessIPv4)
continue
if public:
groups[group].append(''.join(public))
continue
if private:
groups[group].append(''.join(private))
continue
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))
Expand All @@ -105,25 +211,9 @@ def nova_load_config_file():
elif len(sys.argv) == 3 and (sys.argv[1] == '--host'):
results = {}
ips = []
for instance in client.servers.list():
private = openstack_find_nova_addresses(getattr(instance, 'addresses'), 'fixed', 'private')
public = openstack_find_nova_addresses(getattr(instance, 'addresses'), 'floating', 'public')
ips.append( instance.accessIPv4)
ips.append(''.join(private))
ips.append(''.join(public))
if sys.argv[2] in ips:
for key in vars(instance):
# Extract value
value = getattr(instance, key)

# Generate sanitized key
key = 'os_' + re.sub("[^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

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)

Expand Down