summaryrefslogtreecommitdiffstats
path: root/inventory/openstack/hosts/nova.py
blob: d5bd8d1ee83ea3d7a3b1742a6a3586effb7e6f01 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
#!/usr/bin/env python2

# pylint: skip-file

# (c) 2012, Marco Vito Moscaritolo <marco@agavee.com>
#
# 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 <http://www.gnu.org/licenses/>.

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.getcwd() + "/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 <hostname>"
    sys.exit(1)