<NME> terraform.py
<BEF> #!/usr/bin/env python
#
# Copyright 2015 Cisco Systems, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""\
Dynamic inventory for Terraform - finds all `.tfstate` files below the working
directory and generates an inventory based on them.
"""
from __future__ import unicode_literals, print_function
import argparse
from collections import defaultdict
from functools import wraps
import json
import os
import re

VERSION = '0.3.0pre'


def tfstates(root=None):
    root = root or os.getcwd()
    for dirpath, _, filenames in os.walk(root):
        for name in filenames:
            if os.path.splitext(name)[-1] == '.tfstate':
                yield os.path.join(dirpath, name)


def iterresources(filenames):
    for filename in filenames:
        with open(filename, 'r') as json_file:
            state = json.load(json_file)
            for module in state['modules']:
                name = module['path'][-1]
                for key, resource in module['resources'].items():
                    yield name, key, resource

## READ RESOURCES
PARSERS = {}


def _clean_dc(dcname):
    # Consul DCs are strictly alphanumeric with underscores and hyphens -
    # ensure that the consul_dc attribute meets these requirements.
    return re.sub('[^\w_\-]', '-', dcname)


def iterhosts(resources):
    '''yield host tuples of (name, attributes, groups)'''
    for module_name, key, resource in resources:
        resource_type, name = key.split('.', 1)
        try:
            parser = PARSERS[resource_type]
        except KeyError:
            continue

        yield parser(resource, module_name)


def parses(prefix):
    def inner(func):
        PARSERS[prefix] = func
        return func

    return inner


def calculate_mantl_vars(func):
    """calculate Mantl vars"""

    @wraps(func)
    def inner(*args, **kwargs):
        name, attrs, groups = func(*args, **kwargs)

        # attrs
        if attrs.get('role', '') == 'control':
            attrs['consul_is_server'] = True
        else:
            attrs['consul_is_server'] = False

        # groups
        if attrs.get('publicly_routable', False):
            groups.append('publicly_routable')

        return name, attrs, groups

def parse_list(source, prefix):
    return [value for _, value in _parse_prefix(source, prefix)]


@parses('google_compute_instance')
def gce_host(resource, tfvars=None):


def parse_bool(string_form):
    token = string_form.lower()[0]

    if token == 't':
        return True
    elif token == 'f':
        return False
    else:
        raise ValueError('could not convert %r to a bool' % string_form)


@parses('triton_machine')
@calculate_mantl_vars
def triton_machine(resource, module_name):
    raw_attrs = resource['primary']['attributes']
    name = raw_attrs.get('name')
    groups = []

    attrs = {
        'id': raw_attrs['id'],
        'dataset': raw_attrs['dataset'],
        'disk': raw_attrs['disk'],
        'firewall_enabled': parse_bool(raw_attrs['firewall_enabled']),
        'image': raw_attrs['image'],
        'ips': parse_list(raw_attrs, 'ips'),
        'memory': raw_attrs['memory'],
        'name': raw_attrs['name'],
        'networks': parse_list(raw_attrs, 'networks'),
        'package': raw_attrs['package'],
        'primary_ip': raw_attrs['primaryip'],
        'root_authorized_keys': raw_attrs['root_authorized_keys'],
        'state': raw_attrs['state'],
        'tags': parse_dict(raw_attrs, 'tags'),
        'type': raw_attrs['type'],
        'user_data': raw_attrs['user_data'],
        'user_script': raw_attrs['user_script'],

        # ansible
        'ansible_ssh_host': raw_attrs['primaryip'],
        'ansible_ssh_port': 22,
        'ansible_ssh_user': 'root',  # it's "root" on Triton by default

        # generic
        'public_ipv4': raw_attrs['primaryip'],
        'provider': 'triton',
    }

    # private IPv4
    for ip in attrs['ips']:
        if ip.startswith('10') or ip.startswith('192.168'): # private IPs
            attrs['private_ipv4'] = ip
            break

    if 'private_ipv4' not in attrs:
        attrs['private_ipv4'] = attrs['public_ipv4']

    # attrs specific to Mantl
    attrs.update({
        'consul_dc': _clean_dc(attrs['tags'].get('dc', 'none')),
        'role': attrs['tags'].get('role', 'none'),
        'ansible_python_interpreter': attrs['tags'].get('python_bin', 'python')
    })

    # add groups based on attrs
    groups.append('triton_image=' + attrs['image'])
    groups.append('triton_package=' + attrs['package'])
    groups.append('triton_state=' + attrs['state'])
    groups.append('triton_firewall_enabled=%s' % attrs['firewall_enabled'])
    groups.extend('triton_tags_%s=%s' % item
                  for item in attrs['tags'].items())
    groups.extend('triton_network=' + network
                  for network in attrs['networks'])

    # groups specific to Mantl
    groups.append('role=' + attrs['role'])
    groups.append('dc=' + attrs['consul_dc'])

    return name, attrs, groups


@parses('digitalocean_droplet')
@calculate_mantl_vars
def digitalocean_host(resource, tfvars=None):
    raw_attrs = resource['primary']['attributes']
    name = raw_attrs['name']
    groups = []

    attrs = {
        'id': raw_attrs['id'],
        'image': raw_attrs['image'],
        'ipv4_address': raw_attrs['ipv4_address'],
        'locked': parse_bool(raw_attrs['locked']),
        'metadata': json.loads(raw_attrs.get('user_data', '{}')),
        'region': raw_attrs['region'],
        'size': raw_attrs['size'],
        'ssh_keys': parse_list(raw_attrs, 'ssh_keys'),
        'status': raw_attrs['status'],
        # ansible
        'ansible_ssh_host': raw_attrs['ipv4_address'],
        'ansible_ssh_port': 22,
        'ansible_ssh_user': 'root',  # it's always "root" on DO
        # generic
        'public_ipv4': raw_attrs['ipv4_address'],
        'private_ipv4': raw_attrs.get('ipv4_address_private',
                                      raw_attrs['ipv4_address']),
        'provider': 'digitalocean',
    }

    # attrs specific to Mantl
    attrs.update({
        'consul_dc': _clean_dc(attrs['metadata'].get('dc', attrs['region'])),
        'role': attrs['metadata'].get('role', 'none'),
        'ansible_python_interpreter': attrs['metadata'].get('python_bin','python')
    })

    # add groups based on attrs
    groups.append('do_image=' + attrs['image'])
    groups.append('do_locked=%s' % attrs['locked'])
    groups.append('do_region=' + attrs['region'])
    groups.append('do_size=' + attrs['size'])
    groups.append('do_status=' + attrs['status'])
    groups.extend('do_metadata_%s=%s' % item
                  for item in attrs['metadata'].items())

    # groups specific to Mantl
    groups.append('role=' + attrs['role'])
    groups.append('dc=' + attrs['consul_dc'])

    return name, attrs, groups


@parses('softlayer_virtualserver')
@calculate_mantl_vars
def softlayer_host(resource, module_name):
    raw_attrs = resource['primary']['attributes']
    name = raw_attrs['name']
    groups = []

    attrs = {
        'id': raw_attrs['id'],
        'image': raw_attrs['image'],
        'ipv4_address': raw_attrs['ipv4_address'],
        'metadata': json.loads(raw_attrs.get('user_data', '{}')),
        'region': raw_attrs['region'],
        'ram': raw_attrs['ram'],
        'cpu': raw_attrs['cpu'],
        'ssh_keys': parse_list(raw_attrs, 'ssh_keys'),
        'public_ipv4': raw_attrs['ipv4_address'],
        'private_ipv4': raw_attrs['ipv4_address_private'],
        'ansible_ssh_host': raw_attrs['ipv4_address'],
        'ansible_ssh_port': 22,
        'ansible_ssh_user': 'root',
        'provider': 'softlayer',
    }

    # attrs specific to Mantl
    attrs.update({
        'consul_dc': _clean_dc(attrs['metadata'].get('dc', attrs['region'])),
        'role': attrs['metadata'].get('role', 'none'),
        'ansible_python_interpreter': attrs['metadata'].get('python_bin','python')
    })

    # groups specific to Mantl
    groups.append('role=' + attrs['role'])
    groups.append('dc=' + attrs['consul_dc'])

    return name, attrs, groups


@parses('openstack_compute_instance_v2')
@calculate_mantl_vars
def openstack_host(resource, module_name):
    raw_attrs = resource['primary']['attributes']
    name = raw_attrs['name']
    groups = []

    attrs = {
        'access_ip_v4': raw_attrs['access_ip_v4'],
        'access_ip_v6': raw_attrs['access_ip_v6'],
        'flavor': parse_dict(raw_attrs, 'flavor',
                             sep='_'),
        'id': raw_attrs['id'],
        'image': parse_dict(raw_attrs, 'image',
                            sep='_'),
        'key_pair': raw_attrs['key_pair'],
        'metadata': parse_dict(raw_attrs, 'metadata'),
        'network': parse_attr_list(raw_attrs, 'network'),
        'region': raw_attrs.get('region', ''),
        'security_groups': parse_list(raw_attrs, 'security_groups'),
        # ansible
        'ansible_ssh_port': 22,
        # workaround for an OpenStack bug where hosts have a different domain
        # after they're restarted
        'host_domain': 'novalocal',
        'use_host_domain': True,
        # generic
        'public_ipv4': raw_attrs['access_ip_v4'],
        'private_ipv4': raw_attrs['access_ip_v4'],
        'provider': 'openstack',
    }

    if 'floating_ip' in raw_attrs:
        attrs['private_ipv4'] = raw_attrs['network.0.fixed_ip_v4']

    try:
        attrs.update({
            'ansible_ssh_host': raw_attrs['access_ip_v4'],
            'publicly_routable': True,
        })
    except (KeyError, ValueError):
        attrs.update({'ansible_ssh_host': '', 'publicly_routable': False})

    # attrs specific to Ansible
    if 'metadata.ssh_user' in raw_attrs:
        attrs['ansible_ssh_user'] = raw_attrs['metadata.ssh_user']

    # attrs specific to Mantl
    attrs.update({
        'consul_dc': _clean_dc(attrs['metadata'].get('dc', module_name)),
        'role': attrs['metadata'].get('role', 'none'),
        'ansible_python_interpreter': attrs['metadata'].get('python_bin','python')
    })

    # add groups based on attrs
    groups.append('os_image=' + attrs['image']['name'])
    groups.append('os_flavor=' + attrs['flavor']['name'])
    groups.extend('os_metadata_%s=%s' % item
                  for item in attrs['metadata'].items())
    groups.append('os_region=' + attrs['region'])

    # groups specific to Mantl
    groups.append('role=' + attrs['metadata'].get('role', 'none'))
    groups.append('dc=' + attrs['consul_dc'])

    return name, attrs, groups


@parses('aws_instance')
@calculate_mantl_vars
def aws_host(resource, module_name):
    name = resource['primary']['attributes']['tags.Name']
    raw_attrs = resource['primary']['attributes']

    groups = []

    attrs = {
        'ami': raw_attrs['ami'],
        'availability_zone': raw_attrs['availability_zone'],
        'ebs_block_device': parse_attr_list(raw_attrs, 'ebs_block_device'),
        'ebs_optimized': parse_bool(raw_attrs['ebs_optimized']),
        'ephemeral_block_device': parse_attr_list(raw_attrs,
                                                  'ephemeral_block_device'),
        'id': raw_attrs['id'],
        'key_name': raw_attrs['key_name'],
        'private': parse_dict(raw_attrs, 'private',
                              sep='_'),
        'public': parse_dict(raw_attrs, 'public',
                             sep='_'),
        'root_block_device': parse_attr_list(raw_attrs, 'root_block_device'),
        'security_groups': parse_list(raw_attrs, 'security_groups'),
        'subnet': parse_dict(raw_attrs, 'subnet',
                             sep='_'),
        'tags': parse_dict(raw_attrs, 'tags'),
        'tenancy': raw_attrs['tenancy'],
        'vpc_security_group_ids': parse_list(raw_attrs,
                                             'vpc_security_group_ids'),
        # ansible-specific
        'ansible_ssh_port': 22,
        'ansible_ssh_host': raw_attrs['public_ip'],
        # generic
        'public_ipv4': raw_attrs['public_ip'],
        'private_ipv4': raw_attrs['private_ip'],
        'provider': 'aws',
    }

    # attrs specific to Ansible
    if 'tags.sshUser' in raw_attrs:
        attrs['ansible_ssh_user'] = raw_attrs['tags.sshUser']
    if 'tags.sshPrivateIp' in raw_attrs:
        attrs['ansible_ssh_host'] = raw_attrs['private_ip']

    # attrs specific to Mantl
    attrs.update({
        'consul_dc': _clean_dc(attrs['tags'].get('dc', module_name)),
        'role': attrs['tags'].get('role', 'none'),
        'ansible_python_interpreter': attrs['tags'].get('python_bin','python')
    })

    # groups specific to Mantl
    groups.extend(['aws_ami=' + attrs['ami'],
                   'aws_az=' + attrs['availability_zone'],
                   'aws_key_name=' + attrs['key_name'],
                   'aws_tenancy=' + attrs['tenancy']])
    groups.extend('aws_tag_%s=%s' % item for item in attrs['tags'].items())
    groups.extend('aws_vpc_security_group=' + group
                  for group in attrs['vpc_security_group_ids'])
    groups.extend('aws_subnet_%s=%s' % subnet
                  for subnet in attrs['subnet'].items())

    # groups specific to Mantl
    groups.append('role=' + attrs['role'])
    groups.append('dc=' + attrs['consul_dc'])

    return name, attrs, groups


@parses('google_compute_instance')
@calculate_mantl_vars
def gce_host(resource, module_name):
    name = resource['primary']['id']
    raw_attrs = resource['primary']['attributes']
    groups = []

    # network interfaces
    interfaces = parse_attr_list(raw_attrs, 'network_interface')
    for interface in interfaces:
        interface['access_config'] = parse_attr_list(interface,
                                                     'access_config')
        for key in interface.keys():
            if '.' in key:
                del interface[key]

    # general attrs
    attrs = {
        'can_ip_forward': raw_attrs['can_ip_forward'] == 'true',
        'disks': parse_attr_list(raw_attrs, 'disk'),
        'machine_type': raw_attrs['machine_type'],
        'metadata': parse_dict(raw_attrs, 'metadata'),
        'network': parse_attr_list(raw_attrs, 'network'),
        'network_interface': interfaces,
        'self_link': raw_attrs['self_link'],
        'service_account': parse_attr_list(raw_attrs, 'service_account'),
        'tags': parse_list(raw_attrs, 'tags'),
        'zone': raw_attrs['zone'],
        # ansible
        'ansible_ssh_port': 22,
        'provider': 'gce',
    }

    # attrs specific to Ansible
    if 'metadata.ssh_user' in raw_attrs:
        attrs['ansible_ssh_user'] = raw_attrs['metadata.ssh_user']

    # attrs specific to Mantl
    attrs.update({
        'consul_dc': _clean_dc(attrs['metadata'].get('dc', module_name)),
        'role': attrs['metadata'].get('role', 'none'),
        'ansible_python_interpreter': attrs['metadata'].get('python_bin','python')
    })

    try:
        attrs.update({
            'ansible_ssh_host': interfaces[0]['access_config'][0]['nat_ip'] or interfaces[0]['access_config'][0]['assigned_nat_ip'],
            'public_ipv4': interfaces[0]['access_config'][0]['nat_ip'] or interfaces[0]['access_config'][0]['assigned_nat_ip'],
            'private_ipv4': interfaces[0]['address'],
            'publicly_routable': True,
        })
    except (KeyError, ValueError):
        attrs.update({'ansible_ssh_host': '', 'publicly_routable': False})

    # add groups based on attrs
    groups.extend('gce_image=' + disk['image'] for disk in attrs['disks'])
    groups.append('gce_machine_type=' + attrs['machine_type'])
    groups.extend('gce_metadata_%s=%s' % (key, value)
                  for (key, value) in attrs['metadata'].items()
                  if key not in set(['sshKeys']))
    groups.extend('gce_tag=' + tag for tag in attrs['tags'])
    groups.append('gce_zone=' + attrs['zone'])

    if attrs['can_ip_forward']:
        groups.append('gce_ip_forward')
    if attrs['publicly_routable']:
        groups.append('gce_publicly_routable')

    # groups specific to Mantl
    groups.append('role=' + attrs['metadata'].get('role', 'none'))
    groups.append('dc=' + attrs['consul_dc'])

    return name, attrs, groups


@parses('vsphere_virtual_machine')
@calculate_mantl_vars
def vsphere_host(resource, module_name):
    raw_attrs = resource['primary']['attributes']
    network_attrs = parse_dict(raw_attrs, 'network_interface')
    network = parse_dict(network_attrs, '0')
    ip_address = network.get('ipv4_address', network['ip_address'])
    name = raw_attrs['name']
    groups = []

    attrs = {
        'id': raw_attrs['id'],
        'ip_address': ip_address,
        'private_ipv4': ip_address,
        'public_ipv4': ip_address,
        'metadata': parse_dict(raw_attrs, 'custom_configuration_parameters'),
        'ansible_ssh_port': 22,
        'provider': 'vsphere',
    }

    try:
        attrs.update({
            'ansible_ssh_host': ip_address,
        })
    except (KeyError, ValueError):
        attrs.update({'ansible_ssh_host': '', })

    attrs.update({
        'consul_dc': _clean_dc(attrs['metadata'].get('consul_dc', module_name)),
        'role': attrs['metadata'].get('role', 'none'),
        'ansible_python_interpreter': attrs['metadata'].get('python_bin','python')
    })

    # attrs specific to Ansible
    if 'ssh_user' in attrs['metadata']:
        attrs['ansible_ssh_user'] = attrs['metadata']['ssh_user']

    groups.append('role=' + attrs['role'])
    groups.append('dc=' + attrs['consul_dc'])

    return name, attrs, groups

@parses('azure_instance')
@calculate_mantl_vars
def azure_host(resource, module_name):
    name = resource['primary']['attributes']['name']
    raw_attrs = resource['primary']['attributes']

    groups = []

    attrs = {
        'automatic_updates': raw_attrs['automatic_updates'],
        'description': raw_attrs['description'],
        'hosted_service_name': raw_attrs['hosted_service_name'],
        'id': raw_attrs['id'],
        'image': raw_attrs['image'],
        'ip_address': raw_attrs['ip_address'],
        'location': raw_attrs['location'],
        'name': raw_attrs['name'],
        'reverse_dns': raw_attrs['reverse_dns'],
        'security_group': raw_attrs['security_group'],
        'size': raw_attrs['size'],
        'ssh_key_thumbprint': raw_attrs['ssh_key_thumbprint'],
        'subnet': raw_attrs['subnet'],
        'username': raw_attrs['username'],
        'vip_address': raw_attrs['vip_address'],
        'virtual_network': raw_attrs['virtual_network'],
        'endpoint': parse_attr_list(raw_attrs, 'endpoint'),
        # ansible
        'ansible_ssh_port': 22,
        'ansible_ssh_user': raw_attrs['username'],
        'ansible_ssh_host': raw_attrs['vip_address'],
    }

    # attrs specific to mantl
    attrs.update({
        'consul_dc': attrs['location'].lower().replace(" ", "-"),
        'role': attrs['description']
    })

    # groups specific to mantl
    groups.extend(['azure_image=' + attrs['image'],
                   'azure_location=' + attrs['location'].lower().replace(" ", "-"),
                   'azure_username=' + attrs['username'],
                   'azure_security_group=' + attrs['security_group']])

    # groups specific to mantl
    groups.append('role=' + attrs['role'])
    groups.append('dc=' + attrs['consul_dc'])

    return name, attrs, groups


@parses('clc_server')
@calculate_mantl_vars
def clc_server(resource, module_name):
    raw_attrs = resource['primary']['attributes']
    name = raw_attrs.get('id')
    groups = []
    md = parse_dict(raw_attrs, 'metadata')
    attrs = {
        'metadata': md,
        'ansible_ssh_port': md.get('ssh_port', 22),
        'ansible_ssh_user': md.get('ssh_user', 'root'),
        'provider': 'clc',
        'publicly_routable': False,
    }

    try:
        attrs.update({
            'public_ipv4': raw_attrs['public_ip_address'],
            'private_ipv4': raw_attrs['private_ip_address'],
            'ansible_ssh_host': raw_attrs['public_ip_address'],
            'publicly_routable': True,
        })
    except (KeyError, ValueError):
        attrs.update({
            'ansible_ssh_host': raw_attrs['private_ip_address'],
            'private_ipv4': raw_attrs['private_ip_address'],
        })

    attrs.update({
        'consul_dc': _clean_dc(attrs['metadata'].get('dc', module_name)),
        'role': attrs['metadata'].get('role', 'none'),
    })

    groups.append('role=' + attrs['role'])
    groups.append('dc=' + attrs['consul_dc'])
    return name, attrs, groups


@parses('ucs_service_profile')
@calculate_mantl_vars
def ucs_host(resource, module_name):
    name = resource['primary']['id']
    raw_attrs = resource['primary']['attributes']
    groups = []

    # general attrs
    attrs = {
        'metadata': parse_dict(raw_attrs, 'metadata'),
        'provider': 'ucs',
    }

    # attrs specific to mantl
    attrs.update({
        'consul_dc': _clean_dc(attrs['metadata'].get('dc', module_name)),
        'role': attrs['metadata'].get('role', 'none'),
    })

    try:
        attrs.update({
            'ansible_ssh_host': raw_attrs['vNIC.0.ip'],
            'public_ipv4': raw_attrs['vNIC.0.ip'],
            'private_ipv4': raw_attrs['vNIC.0.ip']
        })
    except (KeyError, ValueError):
        attrs.update({'ansible_ssh_host': '', 'publicly_routable': False})

    # add groups based on attrs
    groups.append('role=' + attrs['role']) #.get('role', 'none'))

    # groups.append('all:children')
    groups.append('dc=' + attrs['consul_dc'])

    return name, attrs, groups

## QUERY TYPES
def query_host(hosts, target):
    for name, attrs, _ in hosts:
        if name == target:
            return attrs

    return {}


def query_list(hosts):
    groups = defaultdict(dict)
    meta = {}

    for name, attrs, hostgroups in hosts:
        for group in set(hostgroups):
            groups[group].setdefault('hosts', [])
            groups[group]['hosts'].append(name)

        meta[name] = attrs

    groups['_meta'] = {'hostvars': meta}
    return groups


def query_hostfile(hosts):
    out = ['## begin hosts generated by terraform.py ##']
    out.extend(
        '{}\t{}'.format(attrs['ansible_ssh_host'].ljust(16), name)
        for name, attrs, _ in hosts
    )

    out.append('## end hosts generated by terraform.py ##')
    return '\n'.join(out)


def main():
    parser = argparse.ArgumentParser(
        __file__, __doc__,
        formatter_class=argparse.ArgumentDefaultsHelpFormatter, )
    modes = parser.add_mutually_exclusive_group(required=True)
    modes.add_argument('--list',
                       action='store_true',
                       help='list all variables')
    modes.add_argument('--host', help='list variables for a single host')
    modes.add_argument('--version',
                       action='store_true',
                       help='print version and exit')
    modes.add_argument('--hostfile',
                       action='store_true',
                       help='print hosts as a /etc/hosts snippet')
    parser.add_argument('--pretty',
                        action='store_true',
                        help='pretty-print output JSON')
    parser.add_argument('--nometa',
                        action='store_true',
                        help='with --list, exclude hostvars')
    default_root = os.environ.get('TERRAFORM_STATE_ROOT',
                                  os.path.abspath(os.path.join(os.path.dirname(__file__),
                                                               '..', '..', )))
    parser.add_argument('--root',
                        default=default_root,
                        help='custom root to search for `.tfstate`s in')

    args = parser.parse_args()

    if args.version:
        print('%s %s' % (__file__, VERSION))
        parser.exit()

    hosts = iterhosts(iterresources(tfstates(args.root)))
    if args.list:
        output = query_list(hosts)
        if args.nometa:
            del output['_meta']
        print(json.dumps(output, indent=4 if args.pretty else None))
    elif args.host:
        output = query_host(hosts, args.host)
        print(json.dumps(output, indent=4 if args.pretty else None))
    elif args.hostfile:
        output = query_hostfile(hosts)
        print(output)

    parser.exit()


if __name__ == '__main__':
    main()

<MSG> Add basic OpenStack parsing to dynamic inventory

<DFF> @@ -97,6 +97,31 @@ def parse_dict(source, prefix):
 def parse_list(source, prefix):
     return [value for _, value in _parse_prefix(source, prefix)]
 
+@parses('openstack_compute_instance_v2')
+def openstack_host(resource, tfvars=None):
+    raw_attr = resource['primary']['attributes']
+    name = raw_attr['name']
+    groups = []
+
+    attrs = {}
+
+    attrs.update({
+        'consul_dc': raw_attr['region']
+    })
+
+    try:
+        attrs.update({
+            'ansible_ssh_host': raw_attr['access_ip_v4'],
+        })
+    except (KeyError, ValueError):
+        attrs.update({
+            'ansible_ssh_host': '',
+        })
+
+    groups.append('role=%s' % raw_attr['metadata.role'])
+
+    return name, attrs, groups
+
 
 @parses('google_compute_instance')
 def gce_host(resource, tfvars=None):

{'commit': "diff --git a/plugins/inventory/terraform.py b/plugins/inventory/terraform.py\nindex 28d4ed72..0a40301b 100755\n--- a/plugins/inventory/terraform.py\n+++ b/plugins/inventory/terraform.py\n@@ -97,6 +97,31 @@ def parse_dict(source, prefix):\n def parse_list(source, prefix):\n     return [value for _, value in _parse_prefix(source, prefix)]\n \n+@parses('openstack_compute_instance_v2')\n+def openstack_host(resource, tfvars=None):\n+    raw_attr = resource['primary']['attributes']\n+    name = raw_attr['name']\n+    groups = []\n+\n+    attrs = {}\n+\n+    attrs.update({\n+        'consul_dc': raw_attr['region']\n+    })\n+\n+    try:\n+        attrs.update({\n+            'ansible_ssh_host': raw_attr['access_ip_v4'],\n+        })\n+    except (KeyError, ValueError):\n+        attrs.update({\n+            'ansible_ssh_host': '',\n+        })\n+\n+    groups.append('role=%s' % raw_attr['metadata.role'])\n+\n+    return name, attrs, groups\n+\n \n @parses('google_compute_instance')\n def gce_host(resource, tfvars=None):\n", 'commit_subject': 'Add basic OpenStack parsing to dynamic inventory', 'repo_name': 'mantl/mantl', 'committer': {'name': 'Chris Aubuchon', 'email': '6f998dd61e4d40832d1fc193de3bf025e21e6240@gmail.com', 'time_sec': '1431972649', 'tz_offset': '-300', 'date': {'seconds': '1431972649'}}, 'commit_tree': '0319b1f334ba48b34ad5a87cd11e8e8d9d2d1fbc', 'commit_message': 'Add basic OpenStack parsing to dynamic inventory\n', 'language_name': 'Shell', 'license': 'apache-2.0', 'hunks': "@@ -97,6 +97,31 @@ def parse_dict(source, prefix):\n def parse_list(source, prefix):\n     return [value for _, value in _parse_prefix(source, prefix)]\n \n+@parses('openstack_compute_instance_v2')\n+def openstack_host(resource, tfvars=None):\n+    raw_attr = resource['primary']['attributes']\n+    name = raw_attr['name']\n+    groups = []\n+\n+    attrs = {}\n+\n+    attrs.update({\n+        'consul_dc': raw_attr['region']\n+    })\n+\n+    try:\n+        attrs.update({\n+            'ansible_ssh_host': raw_attr['access_ip_v4'],\n+        })\n+    except (KeyError, ValueError):\n+        attrs.update({\n+            'ansible_ssh_host': '',\n+        })\n+\n+    groups.append('role=%s' % raw_attr['metadata.role'])\n+\n+    return name, attrs, groups\n+\n \n @parses('google_compute_instance')\n def gce_host(resource, tfvars=None):\n", 'addition_count': 25, 'deletion_count': 0, 'src_file': 'a/plugins/inventory/terraform.py', 'tgt_file': 'b/plugins/inventory/terraform.py', 'file_extension': '.py', 'before_file': '#!/usr/bin/env python\n#\n# Copyright 2015 Cisco Systems, Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the "License");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an "AS IS" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n"""\\\nDynamic inventory for Terraform - finds all `.tfstate` files below the working\ndirectory and generates an inventory based on them.\n"""\nfrom __future__ import unicode_literals, print_function\nimport argparse\nfrom collections import defaultdict\nfrom functools import wraps\nimport json\nimport os\nimport re\n\nVERSION = \'0.3.0pre\'\n\n\ndef tfstates(root=None):\n    root = root or os.getcwd()\n    for dirpath, _, filenames in os.walk(root):\n        for name in filenames:\n            if os.path.splitext(name)[-1] == \'.tfstate\':\n                yield os.path.join(dirpath, name)\n\n\ndef iterresources(filenames):\n    for filename in filenames:\n        with open(filename, \'r\') as json_file:\n            state = json.load(json_file)\n            for module in state[\'modules\']:\n                name = module[\'path\'][-1]\n                for key, resource in module[\'resources\'].items():\n                    yield name, key, resource\n\n## READ RESOURCES\nPARSERS = {}\n\n\ndef _clean_dc(dcname):\n    # Consul DCs are strictly alphanumeric with underscores and hyphens -\n    # ensure that the consul_dc attribute meets these requirements.\n    return re.sub(\'[^\\w_\\-]\', \'-\', dcname)\n\n\ndef iterhosts(resources):\n    \'\'\'yield host tuples of (name, attributes, groups)\'\'\'\n    for module_name, key, resource in resources:\n        resource_type, name = key.split(\'.\', 1)\n        try:\n            parser = PARSERS[resource_type]\n        except KeyError:\n            continue\n\n        yield parser(resource, module_name)\n\n\ndef parses(prefix):\n    def inner(func):\n        PARSERS[prefix] = func\n        return func\n\n    return inner\n\n\ndef calculate_mantl_vars(func):\n    """calculate Mantl vars"""\n\n    @wraps(func)\n    def inner(*args, **kwargs):\n        name, attrs, groups = func(*args, **kwargs)\n\n        # attrs\n        if attrs.get(\'role\', \'\') == \'control\':\n            attrs[\'consul_is_server\'] = True\n        else:\n            attrs[\'consul_is_server\'] = False\n\n        # groups\n        if attrs.get(\'publicly_routable\', False):\n            groups.append(\'publicly_routable\')\n\n        return name, attrs, groups\n\ndef parse_list(source, prefix):\n    return [value for _, value in _parse_prefix(source, prefix)]\n\n\n@parses(\'google_compute_instance\')\ndef gce_host(resource, tfvars=None):\n\n\ndef parse_bool(string_form):\n    token = string_form.lower()[0]\n\n    if token == \'t\':\n        return True\n    elif token == \'f\':\n        return False\n    else:\n        raise ValueError(\'could not convert %r to a bool\' % string_form)\n\n\n@parses(\'triton_machine\')\n@calculate_mantl_vars\ndef triton_machine(resource, module_name):\n    raw_attrs = resource[\'primary\'][\'attributes\']\n    name = raw_attrs.get(\'name\')\n    groups = []\n\n    attrs = {\n        \'id\': raw_attrs[\'id\'],\n        \'dataset\': raw_attrs[\'dataset\'],\n        \'disk\': raw_attrs[\'disk\'],\n        \'firewall_enabled\': parse_bool(raw_attrs[\'firewall_enabled\']),\n        \'image\': raw_attrs[\'image\'],\n        \'ips\': parse_list(raw_attrs, \'ips\'),\n        \'memory\': raw_attrs[\'memory\'],\n        \'name\': raw_attrs[\'name\'],\n        \'networks\': parse_list(raw_attrs, \'networks\'),\n        \'package\': raw_attrs[\'package\'],\n        \'primary_ip\': raw_attrs[\'primaryip\'],\n        \'root_authorized_keys\': raw_attrs[\'root_authorized_keys\'],\n        \'state\': raw_attrs[\'state\'],\n        \'tags\': parse_dict(raw_attrs, \'tags\'),\n        \'type\': raw_attrs[\'type\'],\n        \'user_data\': raw_attrs[\'user_data\'],\n        \'user_script\': raw_attrs[\'user_script\'],\n\n        # ansible\n        \'ansible_ssh_host\': raw_attrs[\'primaryip\'],\n        \'ansible_ssh_port\': 22,\n        \'ansible_ssh_user\': \'root\',  # it\'s "root" on Triton by default\n\n        # generic\n        \'public_ipv4\': raw_attrs[\'primaryip\'],\n        \'provider\': \'triton\',\n    }\n\n    # private IPv4\n    for ip in attrs[\'ips\']:\n        if ip.startswith(\'10\') or ip.startswith(\'192.168\'): # private IPs\n            attrs[\'private_ipv4\'] = ip\n            break\n\n    if \'private_ipv4\' not in attrs:\n        attrs[\'private_ipv4\'] = attrs[\'public_ipv4\']\n\n    # attrs specific to Mantl\n    attrs.update({\n        \'consul_dc\': _clean_dc(attrs[\'tags\'].get(\'dc\', \'none\')),\n        \'role\': attrs[\'tags\'].get(\'role\', \'none\'),\n        \'ansible_python_interpreter\': attrs[\'tags\'].get(\'python_bin\', \'python\')\n    })\n\n    # add groups based on attrs\n    groups.append(\'triton_image=\' + attrs[\'image\'])\n    groups.append(\'triton_package=\' + attrs[\'package\'])\n    groups.append(\'triton_state=\' + attrs[\'state\'])\n    groups.append(\'triton_firewall_enabled=%s\' % attrs[\'firewall_enabled\'])\n    groups.extend(\'triton_tags_%s=%s\' % item\n                  for item in attrs[\'tags\'].items())\n    groups.extend(\'triton_network=\' + network\n                  for network in attrs[\'networks\'])\n\n    # groups specific to Mantl\n    groups.append(\'role=\' + attrs[\'role\'])\n    groups.append(\'dc=\' + attrs[\'consul_dc\'])\n\n    return name, attrs, groups\n\n\n@parses(\'digitalocean_droplet\')\n@calculate_mantl_vars\ndef digitalocean_host(resource, tfvars=None):\n    raw_attrs = resource[\'primary\'][\'attributes\']\n    name = raw_attrs[\'name\']\n    groups = []\n\n    attrs = {\n        \'id\': raw_attrs[\'id\'],\n        \'image\': raw_attrs[\'image\'],\n        \'ipv4_address\': raw_attrs[\'ipv4_address\'],\n        \'locked\': parse_bool(raw_attrs[\'locked\']),\n        \'metadata\': json.loads(raw_attrs.get(\'user_data\', \'{}\')),\n        \'region\': raw_attrs[\'region\'],\n        \'size\': raw_attrs[\'size\'],\n        \'ssh_keys\': parse_list(raw_attrs, \'ssh_keys\'),\n        \'status\': raw_attrs[\'status\'],\n        # ansible\n        \'ansible_ssh_host\': raw_attrs[\'ipv4_address\'],\n        \'ansible_ssh_port\': 22,\n        \'ansible_ssh_user\': \'root\',  # it\'s always "root" on DO\n        # generic\n        \'public_ipv4\': raw_attrs[\'ipv4_address\'],\n        \'private_ipv4\': raw_attrs.get(\'ipv4_address_private\',\n                                      raw_attrs[\'ipv4_address\']),\n        \'provider\': \'digitalocean\',\n    }\n\n    # attrs specific to Mantl\n    attrs.update({\n        \'consul_dc\': _clean_dc(attrs[\'metadata\'].get(\'dc\', attrs[\'region\'])),\n        \'role\': attrs[\'metadata\'].get(\'role\', \'none\'),\n        \'ansible_python_interpreter\': attrs[\'metadata\'].get(\'python_bin\',\'python\')\n    })\n\n    # add groups based on attrs\n    groups.append(\'do_image=\' + attrs[\'image\'])\n    groups.append(\'do_locked=%s\' % attrs[\'locked\'])\n    groups.append(\'do_region=\' + attrs[\'region\'])\n    groups.append(\'do_size=\' + attrs[\'size\'])\n    groups.append(\'do_status=\' + attrs[\'status\'])\n    groups.extend(\'do_metadata_%s=%s\' % item\n                  for item in attrs[\'metadata\'].items())\n\n    # groups specific to Mantl\n    groups.append(\'role=\' + attrs[\'role\'])\n    groups.append(\'dc=\' + attrs[\'consul_dc\'])\n\n    return name, attrs, groups\n\n\n@parses(\'softlayer_virtualserver\')\n@calculate_mantl_vars\ndef softlayer_host(resource, module_name):\n    raw_attrs = resource[\'primary\'][\'attributes\']\n    name = raw_attrs[\'name\']\n    groups = []\n\n    attrs = {\n        \'id\': raw_attrs[\'id\'],\n        \'image\': raw_attrs[\'image\'],\n        \'ipv4_address\': raw_attrs[\'ipv4_address\'],\n        \'metadata\': json.loads(raw_attrs.get(\'user_data\', \'{}\')),\n        \'region\': raw_attrs[\'region\'],\n        \'ram\': raw_attrs[\'ram\'],\n        \'cpu\': raw_attrs[\'cpu\'],\n        \'ssh_keys\': parse_list(raw_attrs, \'ssh_keys\'),\n        \'public_ipv4\': raw_attrs[\'ipv4_address\'],\n        \'private_ipv4\': raw_attrs[\'ipv4_address_private\'],\n        \'ansible_ssh_host\': raw_attrs[\'ipv4_address\'],\n        \'ansible_ssh_port\': 22,\n        \'ansible_ssh_user\': \'root\',\n        \'provider\': \'softlayer\',\n    }\n\n    # attrs specific to Mantl\n    attrs.update({\n        \'consul_dc\': _clean_dc(attrs[\'metadata\'].get(\'dc\', attrs[\'region\'])),\n        \'role\': attrs[\'metadata\'].get(\'role\', \'none\'),\n        \'ansible_python_interpreter\': attrs[\'metadata\'].get(\'python_bin\',\'python\')\n    })\n\n    # groups specific to Mantl\n    groups.append(\'role=\' + attrs[\'role\'])\n    groups.append(\'dc=\' + attrs[\'consul_dc\'])\n\n    return name, attrs, groups\n\n\n@parses(\'openstack_compute_instance_v2\')\n@calculate_mantl_vars\ndef openstack_host(resource, module_name):\n    raw_attrs = resource[\'primary\'][\'attributes\']\n    name = raw_attrs[\'name\']\n    groups = []\n\n    attrs = {\n        \'access_ip_v4\': raw_attrs[\'access_ip_v4\'],\n        \'access_ip_v6\': raw_attrs[\'access_ip_v6\'],\n        \'flavor\': parse_dict(raw_attrs, \'flavor\',\n                             sep=\'_\'),\n        \'id\': raw_attrs[\'id\'],\n        \'image\': parse_dict(raw_attrs, \'image\',\n                            sep=\'_\'),\n        \'key_pair\': raw_attrs[\'key_pair\'],\n        \'metadata\': parse_dict(raw_attrs, \'metadata\'),\n        \'network\': parse_attr_list(raw_attrs, \'network\'),\n        \'region\': raw_attrs.get(\'region\', \'\'),\n        \'security_groups\': parse_list(raw_attrs, \'security_groups\'),\n        # ansible\n        \'ansible_ssh_port\': 22,\n        # workaround for an OpenStack bug where hosts have a different domain\n        # after they\'re restarted\n        \'host_domain\': \'novalocal\',\n        \'use_host_domain\': True,\n        # generic\n        \'public_ipv4\': raw_attrs[\'access_ip_v4\'],\n        \'private_ipv4\': raw_attrs[\'access_ip_v4\'],\n        \'provider\': \'openstack\',\n    }\n\n    if \'floating_ip\' in raw_attrs:\n        attrs[\'private_ipv4\'] = raw_attrs[\'network.0.fixed_ip_v4\']\n\n    try:\n        attrs.update({\n            \'ansible_ssh_host\': raw_attrs[\'access_ip_v4\'],\n            \'publicly_routable\': True,\n        })\n    except (KeyError, ValueError):\n        attrs.update({\'ansible_ssh_host\': \'\', \'publicly_routable\': False})\n\n    # attrs specific to Ansible\n    if \'metadata.ssh_user\' in raw_attrs:\n        attrs[\'ansible_ssh_user\'] = raw_attrs[\'metadata.ssh_user\']\n\n    # attrs specific to Mantl\n    attrs.update({\n        \'consul_dc\': _clean_dc(attrs[\'metadata\'].get(\'dc\', module_name)),\n        \'role\': attrs[\'metadata\'].get(\'role\', \'none\'),\n        \'ansible_python_interpreter\': attrs[\'metadata\'].get(\'python_bin\',\'python\')\n    })\n\n    # add groups based on attrs\n    groups.append(\'os_image=\' + attrs[\'image\'][\'name\'])\n    groups.append(\'os_flavor=\' + attrs[\'flavor\'][\'name\'])\n    groups.extend(\'os_metadata_%s=%s\' % item\n                  for item in attrs[\'metadata\'].items())\n    groups.append(\'os_region=\' + attrs[\'region\'])\n\n    # groups specific to Mantl\n    groups.append(\'role=\' + attrs[\'metadata\'].get(\'role\', \'none\'))\n    groups.append(\'dc=\' + attrs[\'consul_dc\'])\n\n    return name, attrs, groups\n\n\n@parses(\'aws_instance\')\n@calculate_mantl_vars\ndef aws_host(resource, module_name):\n    name = resource[\'primary\'][\'attributes\'][\'tags.Name\']\n    raw_attrs = resource[\'primary\'][\'attributes\']\n\n    groups = []\n\n    attrs = {\n        \'ami\': raw_attrs[\'ami\'],\n        \'availability_zone\': raw_attrs[\'availability_zone\'],\n        \'ebs_block_device\': parse_attr_list(raw_attrs, \'ebs_block_device\'),\n        \'ebs_optimized\': parse_bool(raw_attrs[\'ebs_optimized\']),\n        \'ephemeral_block_device\': parse_attr_list(raw_attrs,\n                                                  \'ephemeral_block_device\'),\n        \'id\': raw_attrs[\'id\'],\n        \'key_name\': raw_attrs[\'key_name\'],\n        \'private\': parse_dict(raw_attrs, \'private\',\n                              sep=\'_\'),\n        \'public\': parse_dict(raw_attrs, \'public\',\n                             sep=\'_\'),\n        \'root_block_device\': parse_attr_list(raw_attrs, \'root_block_device\'),\n        \'security_groups\': parse_list(raw_attrs, \'security_groups\'),\n        \'subnet\': parse_dict(raw_attrs, \'subnet\',\n                             sep=\'_\'),\n        \'tags\': parse_dict(raw_attrs, \'tags\'),\n        \'tenancy\': raw_attrs[\'tenancy\'],\n        \'vpc_security_group_ids\': parse_list(raw_attrs,\n                                             \'vpc_security_group_ids\'),\n        # ansible-specific\n        \'ansible_ssh_port\': 22,\n        \'ansible_ssh_host\': raw_attrs[\'public_ip\'],\n        # generic\n        \'public_ipv4\': raw_attrs[\'public_ip\'],\n        \'private_ipv4\': raw_attrs[\'private_ip\'],\n        \'provider\': \'aws\',\n    }\n\n    # attrs specific to Ansible\n    if \'tags.sshUser\' in raw_attrs:\n        attrs[\'ansible_ssh_user\'] = raw_attrs[\'tags.sshUser\']\n    if \'tags.sshPrivateIp\' in raw_attrs:\n        attrs[\'ansible_ssh_host\'] = raw_attrs[\'private_ip\']\n\n    # attrs specific to Mantl\n    attrs.update({\n        \'consul_dc\': _clean_dc(attrs[\'tags\'].get(\'dc\', module_name)),\n        \'role\': attrs[\'tags\'].get(\'role\', \'none\'),\n        \'ansible_python_interpreter\': attrs[\'tags\'].get(\'python_bin\',\'python\')\n    })\n\n    # groups specific to Mantl\n    groups.extend([\'aws_ami=\' + attrs[\'ami\'],\n                   \'aws_az=\' + attrs[\'availability_zone\'],\n                   \'aws_key_name=\' + attrs[\'key_name\'],\n                   \'aws_tenancy=\' + attrs[\'tenancy\']])\n    groups.extend(\'aws_tag_%s=%s\' % item for item in attrs[\'tags\'].items())\n    groups.extend(\'aws_vpc_security_group=\' + group\n                  for group in attrs[\'vpc_security_group_ids\'])\n    groups.extend(\'aws_subnet_%s=%s\' % subnet\n                  for subnet in attrs[\'subnet\'].items())\n\n    # groups specific to Mantl\n    groups.append(\'role=\' + attrs[\'role\'])\n    groups.append(\'dc=\' + attrs[\'consul_dc\'])\n\n    return name, attrs, groups\n\n\n@parses(\'google_compute_instance\')\n@calculate_mantl_vars\ndef gce_host(resource, module_name):\n    name = resource[\'primary\'][\'id\']\n    raw_attrs = resource[\'primary\'][\'attributes\']\n    groups = []\n\n    # network interfaces\n    interfaces = parse_attr_list(raw_attrs, \'network_interface\')\n    for interface in interfaces:\n        interface[\'access_config\'] = parse_attr_list(interface,\n                                                     \'access_config\')\n        for key in interface.keys():\n            if \'.\' in key:\n                del interface[key]\n\n    # general attrs\n    attrs = {\n        \'can_ip_forward\': raw_attrs[\'can_ip_forward\'] == \'true\',\n        \'disks\': parse_attr_list(raw_attrs, \'disk\'),\n        \'machine_type\': raw_attrs[\'machine_type\'],\n        \'metadata\': parse_dict(raw_attrs, \'metadata\'),\n        \'network\': parse_attr_list(raw_attrs, \'network\'),\n        \'network_interface\': interfaces,\n        \'self_link\': raw_attrs[\'self_link\'],\n        \'service_account\': parse_attr_list(raw_attrs, \'service_account\'),\n        \'tags\': parse_list(raw_attrs, \'tags\'),\n        \'zone\': raw_attrs[\'zone\'],\n        # ansible\n        \'ansible_ssh_port\': 22,\n        \'provider\': \'gce\',\n    }\n\n    # attrs specific to Ansible\n    if \'metadata.ssh_user\' in raw_attrs:\n        attrs[\'ansible_ssh_user\'] = raw_attrs[\'metadata.ssh_user\']\n\n    # attrs specific to Mantl\n    attrs.update({\n        \'consul_dc\': _clean_dc(attrs[\'metadata\'].get(\'dc\', module_name)),\n        \'role\': attrs[\'metadata\'].get(\'role\', \'none\'),\n        \'ansible_python_interpreter\': attrs[\'metadata\'].get(\'python_bin\',\'python\')\n    })\n\n    try:\n        attrs.update({\n            \'ansible_ssh_host\': interfaces[0][\'access_config\'][0][\'nat_ip\'] or interfaces[0][\'access_config\'][0][\'assigned_nat_ip\'],\n            \'public_ipv4\': interfaces[0][\'access_config\'][0][\'nat_ip\'] or interfaces[0][\'access_config\'][0][\'assigned_nat_ip\'],\n            \'private_ipv4\': interfaces[0][\'address\'],\n            \'publicly_routable\': True,\n        })\n    except (KeyError, ValueError):\n        attrs.update({\'ansible_ssh_host\': \'\', \'publicly_routable\': False})\n\n    # add groups based on attrs\n    groups.extend(\'gce_image=\' + disk[\'image\'] for disk in attrs[\'disks\'])\n    groups.append(\'gce_machine_type=\' + attrs[\'machine_type\'])\n    groups.extend(\'gce_metadata_%s=%s\' % (key, value)\n                  for (key, value) in attrs[\'metadata\'].items()\n                  if key not in set([\'sshKeys\']))\n    groups.extend(\'gce_tag=\' + tag for tag in attrs[\'tags\'])\n    groups.append(\'gce_zone=\' + attrs[\'zone\'])\n\n    if attrs[\'can_ip_forward\']:\n        groups.append(\'gce_ip_forward\')\n    if attrs[\'publicly_routable\']:\n        groups.append(\'gce_publicly_routable\')\n\n    # groups specific to Mantl\n    groups.append(\'role=\' + attrs[\'metadata\'].get(\'role\', \'none\'))\n    groups.append(\'dc=\' + attrs[\'consul_dc\'])\n\n    return name, attrs, groups\n\n\n@parses(\'vsphere_virtual_machine\')\n@calculate_mantl_vars\ndef vsphere_host(resource, module_name):\n    raw_attrs = resource[\'primary\'][\'attributes\']\n    network_attrs = parse_dict(raw_attrs, \'network_interface\')\n    network = parse_dict(network_attrs, \'0\')\n    ip_address = network.get(\'ipv4_address\', network[\'ip_address\'])\n    name = raw_attrs[\'name\']\n    groups = []\n\n    attrs = {\n        \'id\': raw_attrs[\'id\'],\n        \'ip_address\': ip_address,\n        \'private_ipv4\': ip_address,\n        \'public_ipv4\': ip_address,\n        \'metadata\': parse_dict(raw_attrs, \'custom_configuration_parameters\'),\n        \'ansible_ssh_port\': 22,\n        \'provider\': \'vsphere\',\n    }\n\n    try:\n        attrs.update({\n            \'ansible_ssh_host\': ip_address,\n        })\n    except (KeyError, ValueError):\n        attrs.update({\'ansible_ssh_host\': \'\', })\n\n    attrs.update({\n        \'consul_dc\': _clean_dc(attrs[\'metadata\'].get(\'consul_dc\', module_name)),\n        \'role\': attrs[\'metadata\'].get(\'role\', \'none\'),\n        \'ansible_python_interpreter\': attrs[\'metadata\'].get(\'python_bin\',\'python\')\n    })\n\n    # attrs specific to Ansible\n    if \'ssh_user\' in attrs[\'metadata\']:\n        attrs[\'ansible_ssh_user\'] = attrs[\'metadata\'][\'ssh_user\']\n\n    groups.append(\'role=\' + attrs[\'role\'])\n    groups.append(\'dc=\' + attrs[\'consul_dc\'])\n\n    return name, attrs, groups\n\n@parses(\'azure_instance\')\n@calculate_mantl_vars\ndef azure_host(resource, module_name):\n    name = resource[\'primary\'][\'attributes\'][\'name\']\n    raw_attrs = resource[\'primary\'][\'attributes\']\n\n    groups = []\n\n    attrs = {\n        \'automatic_updates\': raw_attrs[\'automatic_updates\'],\n        \'description\': raw_attrs[\'description\'],\n        \'hosted_service_name\': raw_attrs[\'hosted_service_name\'],\n        \'id\': raw_attrs[\'id\'],\n        \'image\': raw_attrs[\'image\'],\n        \'ip_address\': raw_attrs[\'ip_address\'],\n        \'location\': raw_attrs[\'location\'],\n        \'name\': raw_attrs[\'name\'],\n        \'reverse_dns\': raw_attrs[\'reverse_dns\'],\n        \'security_group\': raw_attrs[\'security_group\'],\n        \'size\': raw_attrs[\'size\'],\n        \'ssh_key_thumbprint\': raw_attrs[\'ssh_key_thumbprint\'],\n        \'subnet\': raw_attrs[\'subnet\'],\n        \'username\': raw_attrs[\'username\'],\n        \'vip_address\': raw_attrs[\'vip_address\'],\n        \'virtual_network\': raw_attrs[\'virtual_network\'],\n        \'endpoint\': parse_attr_list(raw_attrs, \'endpoint\'),\n        # ansible\n        \'ansible_ssh_port\': 22,\n        \'ansible_ssh_user\': raw_attrs[\'username\'],\n        \'ansible_ssh_host\': raw_attrs[\'vip_address\'],\n    }\n\n    # attrs specific to mantl\n    attrs.update({\n        \'consul_dc\': attrs[\'location\'].lower().replace(" ", "-"),\n        \'role\': attrs[\'description\']\n    })\n\n    # groups specific to mantl\n    groups.extend([\'azure_image=\' + attrs[\'image\'],\n                   \'azure_location=\' + attrs[\'location\'].lower().replace(" ", "-"),\n                   \'azure_username=\' + attrs[\'username\'],\n                   \'azure_security_group=\' + attrs[\'security_group\']])\n\n    # groups specific to mantl\n    groups.append(\'role=\' + attrs[\'role\'])\n    groups.append(\'dc=\' + attrs[\'consul_dc\'])\n\n    return name, attrs, groups\n\n\n@parses(\'clc_server\')\n@calculate_mantl_vars\ndef clc_server(resource, module_name):\n    raw_attrs = resource[\'primary\'][\'attributes\']\n    name = raw_attrs.get(\'id\')\n    groups = []\n    md = parse_dict(raw_attrs, \'metadata\')\n    attrs = {\n        \'metadata\': md,\n        \'ansible_ssh_port\': md.get(\'ssh_port\', 22),\n        \'ansible_ssh_user\': md.get(\'ssh_user\', \'root\'),\n        \'provider\': \'clc\',\n        \'publicly_routable\': False,\n    }\n\n    try:\n        attrs.update({\n            \'public_ipv4\': raw_attrs[\'public_ip_address\'],\n            \'private_ipv4\': raw_attrs[\'private_ip_address\'],\n            \'ansible_ssh_host\': raw_attrs[\'public_ip_address\'],\n            \'publicly_routable\': True,\n        })\n    except (KeyError, ValueError):\n        attrs.update({\n            \'ansible_ssh_host\': raw_attrs[\'private_ip_address\'],\n            \'private_ipv4\': raw_attrs[\'private_ip_address\'],\n        })\n\n    attrs.update({\n        \'consul_dc\': _clean_dc(attrs[\'metadata\'].get(\'dc\', module_name)),\n        \'role\': attrs[\'metadata\'].get(\'role\', \'none\'),\n    })\n\n    groups.append(\'role=\' + attrs[\'role\'])\n    groups.append(\'dc=\' + attrs[\'consul_dc\'])\n    return name, attrs, groups\n\n\n@parses(\'ucs_service_profile\')\n@calculate_mantl_vars\ndef ucs_host(resource, module_name):\n    name = resource[\'primary\'][\'id\']\n    raw_attrs = resource[\'primary\'][\'attributes\']\n    groups = []\n\n    # general attrs\n    attrs = {\n        \'metadata\': parse_dict(raw_attrs, \'metadata\'),\n        \'provider\': \'ucs\',\n    }\n\n    # attrs specific to mantl\n    attrs.update({\n        \'consul_dc\': _clean_dc(attrs[\'metadata\'].get(\'dc\', module_name)),\n        \'role\': attrs[\'metadata\'].get(\'role\', \'none\'),\n    })\n\n    try:\n        attrs.update({\n            \'ansible_ssh_host\': raw_attrs[\'vNIC.0.ip\'],\n            \'public_ipv4\': raw_attrs[\'vNIC.0.ip\'],\n            \'private_ipv4\': raw_attrs[\'vNIC.0.ip\']\n        })\n    except (KeyError, ValueError):\n        attrs.update({\'ansible_ssh_host\': \'\', \'publicly_routable\': False})\n\n    # add groups based on attrs\n    groups.append(\'role=\' + attrs[\'role\']) #.get(\'role\', \'none\'))\n\n    # groups.append(\'all:children\')\n    groups.append(\'dc=\' + attrs[\'consul_dc\'])\n\n    return name, attrs, groups\n\n## QUERY TYPES\ndef query_host(hosts, target):\n    for name, attrs, _ in hosts:\n        if name == target:\n            return attrs\n\n    return {}\n\n\ndef query_list(hosts):\n    groups = defaultdict(dict)\n    meta = {}\n\n    for name, attrs, hostgroups in hosts:\n        for group in set(hostgroups):\n            groups[group].setdefault(\'hosts\', [])\n            groups[group][\'hosts\'].append(name)\n\n        meta[name] = attrs\n\n    groups[\'_meta\'] = {\'hostvars\': meta}\n    return groups\n\n\ndef query_hostfile(hosts):\n    out = [\'## begin hosts generated by terraform.py ##\']\n    out.extend(\n        \'{}\\t{}\'.format(attrs[\'ansible_ssh_host\'].ljust(16), name)\n        for name, attrs, _ in hosts\n    )\n\n    out.append(\'## end hosts generated by terraform.py ##\')\n    return \'\\n\'.join(out)\n\n\ndef main():\n    parser = argparse.ArgumentParser(\n        __file__, __doc__,\n        formatter_class=argparse.ArgumentDefaultsHelpFormatter, )\n    modes = parser.add_mutually_exclusive_group(required=True)\n    modes.add_argument(\'--list\',\n                       action=\'store_true\',\n                       help=\'list all variables\')\n    modes.add_argument(\'--host\', help=\'list variables for a single host\')\n    modes.add_argument(\'--version\',\n                       action=\'store_true\',\n                       help=\'print version and exit\')\n    modes.add_argument(\'--hostfile\',\n                       action=\'store_true\',\n                       help=\'print hosts as a /etc/hosts snippet\')\n    parser.add_argument(\'--pretty\',\n                        action=\'store_true\',\n                        help=\'pretty-print output JSON\')\n    parser.add_argument(\'--nometa\',\n                        action=\'store_true\',\n                        help=\'with --list, exclude hostvars\')\n    default_root = os.environ.get(\'TERRAFORM_STATE_ROOT\',\n                                  os.path.abspath(os.path.join(os.path.dirname(__file__),\n                                                               \'..\', \'..\', )))\n    parser.add_argument(\'--root\',\n                        default=default_root,\n                        help=\'custom root to search for `.tfstate`s in\')\n\n    args = parser.parse_args()\n\n    if args.version:\n        print(\'%s %s\' % (__file__, VERSION))\n        parser.exit()\n\n    hosts = iterhosts(iterresources(tfstates(args.root)))\n    if args.list:\n        output = query_list(hosts)\n        if args.nometa:\n            del output[\'_meta\']\n        print(json.dumps(output, indent=4 if args.pretty else None))\n    elif args.host:\n        output = query_host(hosts, args.host)\n        print(json.dumps(output, indent=4 if args.pretty else None))\n    elif args.hostfile:\n        output = query_hostfile(hosts)\n        print(output)\n\n    parser.exit()\n\n\nif __name__ == \'__main__\':\n    main()\n', 'message': 'Add basic OpenStack parsing to dynamic inventory\n', 'author_name': 'Chris Aubuchon', 'lang': 'py'}