From ccc607eaa5265ee6295ab74e427e07a765d47d93 Mon Sep 17 00:00:00 2001 From: Paul Durivage Date: Tue, 1 Oct 2013 13:15:20 -0500 Subject: [PATCH 1/8] Documentation updates --- plugins/inventory/rax.py | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/plugins/inventory/rax.py b/plugins/inventory/rax.py index bcfe6745617..cbf781cfea5 100755 --- a/plugins/inventory/rax.py +++ b/plugins/inventory/rax.py @@ -57,22 +57,17 @@ options: - File to find the Rackspace Public Cloud credentials in required: true default: null - region_name: - description: - - Region name to use in request - required: false - default: DFW -author: Jesse Keating +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, ...) + - One environment variable needs to be set: RAX_CREDS_FILE. + - RAX_CREDS_FILE points to a credentials file appropriate for pyrax. + - See https://github.com/rackspace/pyrax/blob/master/docs/getting_started.md#authenticating requirements: [ "pyrax" ] examples: - description: List server instances - 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 + code: RAX_CREDS_FILE=~/.raxpub rax.py --list ''' import sys From 25c3bec6ef70627eba2ca6c0a397bbce2a075fc6 Mon Sep 17 00:00:00 2001 From: Paul Durivage Date: Tue, 1 Oct 2013 13:23:39 -0500 Subject: [PATCH 2/8] Eliminate the RAX_REGION environment variable; iterate through all regions available to an account and present servers in region groups; default to using server name to identify servers in groups; set ansible_ssh_host key for each server; utilize the _meta key to provide all the hostvars for each server; all grouping by web, db, sql, lb, app based on server name; pretty print JSON output for some human-readable inventory action Add set_setting to alleviate requirement that ~/.pyrax.cfg exists Add regions as a optional env var to narrow our inventory scope Clean up --- plugins/inventory/rax.py | 181 +++++++++++++++++++++++++-------------- 1 file changed, 118 insertions(+), 63 deletions(-) diff --git a/plugins/inventory/rax.py b/plugins/inventory/rax.py index cbf781cfea5..de5e5c1cd98 100755 --- a/plugins/inventory/rax.py +++ b/plugins/inventory/rax.py @@ -74,6 +74,7 @@ import sys import re import os import argparse +import collections try: import json @@ -86,69 +87,123 @@ 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)') - -args = parser.parse_args() - -# 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) - -pyrax.set_setting('identity_type', 'rackspace') - -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 = {} - - # 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' - - # Create group if not exist and add the server - groups.setdefault(group, []).append(server.accessIPv4) - - # Return server list - print(json.dumps(groups)) +def host(hostname): + hostvars = {} + + for region in pyrax.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) + + # Generate sanitized key + key = 'rax_' + (re.sub("[^A-Za-z0-9\-]", "_", key) + .lower() + .lstrip("_")) + hostvars[key] = value + + # And finally, add an IP address + hostvars['ansible_ssh_host'] = server.accessIPv4 + print(json.dumps(hostvars, sort_keys=True, indent=4)) + + +def _list(region): + groups = collections.defaultdict(list) + hostvars = collections.defaultdict(dict) + + if region and region.upper() in pyrax.regions: + pyrax.regions = (region,) + + # Go through all the regions looking for servers + for region in pyrax.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) + + # Anything we can discern from the hostname? + try: + subdom = server.name.split('.')[0] + except IndexError: + pass + else: + for name in ('web', 'db', 'sql', 'lb', 'app'): + if name in subdom: + groups[name].append(server.name) + + # 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) + + # 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(): + try: + creds_file = os.environ['RAX_CREDS_FILE'] + region = os.getenv('RAX_REGION') + except KeyError, e: + sys.stderr.write('Unable to load environment ' + 'variable %s\n' % e.message) + 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) + + return region + + +def main(): + args = parse_args() + region = setup() + if args.list: + _list(region) + elif args.host: + host(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() From 16336db9f60146fbdfff58d7571c33fe485729e8 Mon Sep 17 00:00:00 2001 From: Paul Durivage Date: Wed, 2 Oct 2013 20:38:40 -0500 Subject: [PATCH 3/8] Documentation updates for region env var --- plugins/inventory/rax.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/plugins/inventory/rax.py b/plugins/inventory/rax.py index de5e5c1cd98..dd7ac8f15a1 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,6 +57,11 @@ options: - File to find the Rackspace Public Cloud credentials in required: true default: null + region: + description: + - An optional value to narrow inventory scope, i.e. DFW, ORD, IAD, LON + required: false + default: null authors: - Jesse Keating - Paul Durivage @@ -64,10 +69,16 @@ notes: - One environment variable needs to be set: RAX_CREDS_FILE. - RAX_CREDS_FILE points to a credentials file appropriate for pyrax. - 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) 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: Get server details for server named "server.example.com" + code: RAX_CREDS_FILE=~/.raxpub rax.py --host server.example.com ''' import sys From 7c88ac0a5ee2c70da27bb0963e120b9af1b34a3f Mon Sep 17 00:00:00 2001 From: Paul Durivage Date: Thu, 17 Oct 2013 11:18:07 -0500 Subject: [PATCH 4/8] Accept a comma-separated list of regions --- plugins/inventory/rax.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/plugins/inventory/rax.py b/plugins/inventory/rax.py index dd7ac8f15a1..6c6d6666fd0 100755 --- a/plugins/inventory/rax.py +++ b/plugins/inventory/rax.py @@ -70,13 +70,15 @@ notes: - RAX_CREDS_FILE points to a credentials file appropriate for pyrax. - 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) + - 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 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 ''' @@ -128,7 +130,8 @@ def _list(region): hostvars = collections.defaultdict(dict) if region and region.upper() in pyrax.regions: - pyrax.regions = (region,) + pyrax.regions = (region.upper() for region in region.split(',')) + # Go through all the regions looking for servers for region in pyrax.regions: From d59f9b4289ea94bdf257bcea51786cc4cc428617 Mon Sep 17 00:00:00 2001 From: Paul Durivage Date: Thu, 17 Oct 2013 11:19:00 -0500 Subject: [PATCH 5/8] Remove grouping of hosts by trying to discern subdom in server name --- plugins/inventory/rax.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/plugins/inventory/rax.py b/plugins/inventory/rax.py index 6c6d6666fd0..162884b2719 100755 --- a/plugins/inventory/rax.py +++ b/plugins/inventory/rax.py @@ -141,16 +141,6 @@ def _list(region): # Create a group on region groups[region].append(server.name) - # Anything we can discern from the hostname? - try: - subdom = server.name.split('.')[0] - except IndexError: - pass - else: - for name in ('web', 'db', 'sql', 'lb', 'app'): - if name in subdom: - groups[name].append(server.name) - # Check if group metadata key in servers' metadata try: group = server.metadata['group'] From 859c6c6d445a920113d4df2112523202c1e68793 Mon Sep 17 00:00:00 2001 From: Paul Durivage Date: Thu, 17 Oct 2013 11:55:38 -0500 Subject: [PATCH 6/8] Make creds file env var optional; look for ~/.rackspace_cloud_credentials if not supplied --- plugins/inventory/rax.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/plugins/inventory/rax.py b/plugins/inventory/rax.py index 162884b2719..59c86917b88 100755 --- a/plugins/inventory/rax.py +++ b/plugins/inventory/rax.py @@ -66,8 +66,8 @@ authors: - Jesse Keating - Paul Durivage notes: - - One environment variable needs to be set: RAX_CREDS_FILE. - - RAX_CREDS_FILE points to a credentials file appropriate for pyrax. + - 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 @@ -181,14 +181,22 @@ def 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'] - region = os.getenv('RAX_REGION') except KeyError, e: - sys.stderr.write('Unable to load environment ' - 'variable %s\n' % e.message) - sys.exit(1) - + # 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) + + region = os.getenv('RAX_REGION') pyrax.set_setting('identity_type', 'rackspace') try: From 3bfdaad06091d193a9b9c77d267fefe922cfb356 Mon Sep 17 00:00:00 2001 From: Paul Durivage Date: Sat, 19 Oct 2013 14:19:30 -0500 Subject: [PATCH 7/8] Implement @cchurch suggestions --- plugins/inventory/rax.py | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/plugins/inventory/rax.py b/plugins/inventory/rax.py index 59c86917b88..9e7c906e2f9 100755 --- a/plugins/inventory/rax.py +++ b/plugins/inventory/rax.py @@ -101,10 +101,10 @@ except ImportError: sys.exit(1) -def host(hostname): +def host(regions, hostname): hostvars = {} - for region in pyrax.regions: + for region in regions: # Connect to the region cs = pyrax.connect_to_cloudservers(region=region) for server in cs.servers.list(): @@ -125,16 +125,12 @@ def host(hostname): print(json.dumps(hostvars, sort_keys=True, indent=4)) -def _list(region): +def _list(regions): groups = collections.defaultdict(list) hostvars = collections.defaultdict(dict) - if region and region.upper() in pyrax.regions: - pyrax.regions = (region.upper() for region in region.split(',')) - - # Go through all the regions looking for servers - for region in pyrax.regions: + for region in regions: # Connect to the region cs = pyrax.connect_to_cloudservers(region=region) for server in cs.servers.list(): @@ -196,7 +192,18 @@ def setup(): % (e.message, default_creds_file)) sys.exit(1) - region = os.getenv('RAX_REGION') + 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) + pyrax.set_setting('identity_type', 'rackspace') try: @@ -205,17 +212,17 @@ def setup(): sys.stderr.write("%s: %s\n" % (e, e.message)) sys.exit(1) - return region + return regions def main(): args = parse_args() - region = setup() + regions = setup() if args.list: - _list(region) + _list(regions) elif args.host: - host(args.host) + host(regions, args.host) sys.exit(0) if __name__ == '__main__': - main() + main() \ No newline at end of file From f63a6fafe9d6c471dfb34144168ed70d6951dee1 Mon Sep 17 00:00:00 2001 From: Paul Durivage Date: Sat, 19 Oct 2013 14:57:23 -0500 Subject: [PATCH 8/8] Move regions until after authentication; otherwise pyrax.regions is an empty tuple and no inventory is returned. --- plugins/inventory/rax.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/plugins/inventory/rax.py b/plugins/inventory/rax.py index 9e7c906e2f9..6836db61f66 100755 --- a/plugins/inventory/rax.py +++ b/plugins/inventory/rax.py @@ -86,6 +86,7 @@ examples: import sys import re import os + import argparse import collections @@ -192,6 +193,14 @@ def setup(): % (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() @@ -204,14 +213,6 @@ def setup(): elif region not in regions: regions.append(region) - 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) - return regions