diff --git a/plugins/inventory/rax.py b/plugins/inventory/rax.py index bcfe674561..6836db61f6 100755 --- a/plugins/inventory/rax.py +++ b/plugins/inventory/rax.py @@ -46,7 +46,7 @@ description: rax_name rax_created rax_tenant_id - rax__loaded + rax_loaded where some item can have nested structure. - credentials are set in a credentials file @@ -57,28 +57,38 @@ options: - File to find the Rackspace Public Cloud credentials in required: true default: null - region_name: + region: description: - - Region name to use in request - required: false - default: DFW -author: Jesse Keating + - An optional value to narrow inventory scope, i.e. DFW, ORD, IAD, LON + required: false + default: null +authors: + - Jesse Keating + - Paul Durivage notes: - - Two environment variables need to be set, RAX_CREDS and RAX_REGION. - - RAX_CREDS points to a credentials file appropriate for pyrax - - RAX_REGION defines a Rackspace Public Cloud region (DFW, ORD, LON, ...) + - RAX_CREDS_FILE is an optional environment variable that points to a pyrax-compatible credentials file. + - If RAX_CREDS_FILE is not supplied, rax.py will look for a credentials file at ~/.rackspace_cloud_credentials. + - See https://github.com/rackspace/pyrax/blob/master/docs/getting_started.md#authenticating + - RAX_REGION is an optional environment variable to narrow inventory search scope + - RAX_REGION, if used, needs a value like ORD, DFW, SYD (a Rackspace datacenter) and optionally accepts a comma-separated list requirements: [ "pyrax" ] examples: - description: List server instances + code: RAX_CREDS_FILE=~/.raxpub rax.py --list + - description: List servers in ORD datacenter only code: RAX_CREDS_FILE=~/.raxpub RAX_REGION=ORD rax.py --list - - description: List server instance properties - code: RAX_CREDS_FILE=~/.raxpub RAX_REGION=ORD rax.py --host + - description: List servers in ORD and DFW datacenters + code: RAX_CREDS_FILE=~/.raxpub RAX_REGION=ORD,DFW rax.py --list + - description: Get server details for server named "server.example.com" + code: RAX_CREDS_FILE=~/.raxpub rax.py --host server.example.com ''' import sys import re import os + import argparse +import collections try: import json @@ -91,69 +101,129 @@ except ImportError: print('pyrax required for this module') sys.exit(1) -# Setup the parser -parser = argparse.ArgumentParser(description='List active instances', - epilog='List by itself will list all the active \ - instances. Listing a specific instance will show \ - all the details about the instance.') -parser.add_argument('--list', action='store_true', default=True, - help='List active servers') -parser.add_argument('--host', - help='List details about the specific host (IP address)') +def host(regions, hostname): + hostvars = {} -args = parser.parse_args() + for region in regions: + # Connect to the region + cs = pyrax.connect_to_cloudservers(region=region) + for server in cs.servers.list(): + if server.name == hostname: + keys = [key for key in vars(server) if key not in ('manager', '_info')] + for key in keys: + # Extract value + value = getattr(server, key) -# setup the auth -try: - creds_file = os.environ['RAX_CREDS_FILE'] - region = os.environ['RAX_REGION'] -except KeyError, e: - sys.stderr.write('Unable to load %s\n' % e.message) - sys.exit(1) + # Generate sanitized key + key = 'rax_' + (re.sub("[^A-Za-z0-9\-]", "_", key) + .lower() + .lstrip("_")) + hostvars[key] = value -pyrax.set_setting('identity_type', 'rackspace') + # And finally, add an IP address + hostvars['ansible_ssh_host'] = server.accessIPv4 + print(json.dumps(hostvars, sort_keys=True, indent=4)) -try: - pyrax.set_setting("identity_type", "rackspace") - pyrax.set_credential_file(os.path.expanduser(creds_file), - region=region) -except Exception, e: - sys.stderr.write("%s: %s\n" % (e, e.message)) - sys.exit(1) -# Execute the right stuff -if not args.host: - groups = {} +def _list(regions): + groups = collections.defaultdict(list) + hostvars = collections.defaultdict(dict) - # Cycle on servers - for server in pyrax.cloudservers.list(): - # Define group (or set to empty string) - try: - group = server.metadata['group'] - except KeyError: - group = 'undefined' + # Go through all the regions looking for servers + for region in regions: + # Connect to the region + cs = pyrax.connect_to_cloudservers(region=region) + for server in cs.servers.list(): + # Create a group on region + groups[region].append(server.name) - # Create group if not exist and add the server - groups.setdefault(group, []).append(server.accessIPv4) + # Check if group metadata key in servers' metadata + try: + group = server.metadata['group'] + except KeyError: + pass + else: + # Create group if not exist and add the server + groups[group].append(server.name) - # Return server list - print(json.dumps(groups)) + # Add host metadata + keys = [key for key in vars(server) if key not in ('manager', '_info')] + for key in keys: + # Extract value + value = getattr(server, key) + + # Generate sanitized key + key = 'rax_' + (re.sub("[^A-Za-z0-9\-]", "_", key) + .lower() + .lstrip('_')) + hostvars[server.name][key] = value + + # And finally, add an IP address + hostvars[server.name]['ansible_ssh_host'] = server.accessIPv4 + + if hostvars: + groups['_meta'] = {'hostvars': hostvars} + print(json.dumps(groups, sort_keys=True, indent=4)) + + +def parse_args(): + parser = argparse.ArgumentParser(description='Ansible Rackspace Cloud ' + 'inventory module') + group = parser.add_mutually_exclusive_group(required=True) + group.add_argument('--list', action='store_true', + help='List active servers') + group.add_argument('--host', help='List details about the specific host') + return parser.parse_args() + + +def setup(): + default_creds_file = os.path.expanduser('~/.rackspace_cloud_credentials') + + # Attempt to grab credentials from environment first + try: + creds_file = os.environ['RAX_CREDS_FILE'] + except KeyError, e: + # But if that fails, use the default location of ~/.rackspace_cloud_credentials + if os.path.isfile(default_creds_file): + creds_file = default_creds_file + else: + sys.stderr.write('No value in environment variable %s and/or no ' + 'credentials file at %s\n' + % (e.message, default_creds_file)) + sys.exit(1) + + pyrax.set_setting('identity_type', 'rackspace') + + try: + pyrax.set_credential_file(os.path.expanduser(creds_file)) + except Exception, e: + sys.stderr.write("%s: %s\n" % (e, e.message)) + sys.exit(1) + + regions = [] + for region in os.getenv('RAX_REGION', 'all').split(','): + region = region.strip().upper() + if region == 'ALL': + regions = pyrax.regions + break + elif region not in pyrax.regions: + sys.stderr.write('Unsupported region %s' % region) + sys.exit(1) + elif region not in regions: + regions.append(region) + + return regions + + +def main(): + args = parse_args() + regions = setup() + if args.list: + _list(regions) + elif args.host: + host(regions, args.host) sys.exit(0) -# Get the deets for the instance asked for -results = {} -# This should be only one, but loop anyway -for server in pyrax.cloudservers.list(): - if server.accessIPv4 == args.host: - for key in [key for key in vars(server) if - key not in ('manager', '_info')]: - # Extract value - value = getattr(server, key) - - # Generate sanitized key - key = 'rax_' + re.sub("[^A-Za-z0-9\-]", "_", key).lower() - results[key] = value - -print(json.dumps(results)) -sys.exit(0) +if __name__ == '__main__': + main() \ No newline at end of file