From 4036df11cf19ad51ddb2ad9205b655d922c0110b Mon Sep 17 00:00:00 2001 From: Chris Church Date: Tue, 1 Jul 2014 15:29:39 -0400 Subject: [PATCH] Add configurable option to support nested inventory groups. --- plugins/inventory/ec2.ini | 3 ++ plugins/inventory/ec2.py | 77 ++++++++++++++++++++++++++++++++------- 2 files changed, 67 insertions(+), 13 deletions(-) diff --git a/plugins/inventory/ec2.ini b/plugins/inventory/ec2.ini index 8a0c3ad45c8..f024e87265a 100644 --- a/plugins/inventory/ec2.ini +++ b/plugins/inventory/ec2.ini @@ -53,3 +53,6 @@ cache_path = ~/.ansible/tmp # seconds, a new API call will be made, and the cache file will be updated. # To disable the cache, set this value to 0 cache_max_age = 300 + +# Organize groups into a nested/hierarchy instead of a flat namespace. +nested_groups = False diff --git a/plugins/inventory/ec2.py b/plugins/inventory/ec2.py index 84841d3f09a..af71191df84 100755 --- a/plugins/inventory/ec2.py +++ b/plugins/inventory/ec2.py @@ -230,8 +230,12 @@ class Ec2Inventory(object): self.cache_path_cache = cache_dir + "/ansible-ec2.cache" self.cache_path_index = cache_dir + "/ansible-ec2.index" self.cache_max_age = config.getint('ec2', 'cache_max_age') - + # Configure nested groups instead of flat namespace. + if config.has_option('ec2', 'nested_groups'): + self.nested_groups = config.getboolean('ec2', 'nested_groups') + else: + self.nested_groups = False def parse_cli_args(self): ''' Command line argument processing ''' @@ -321,7 +325,6 @@ class Ec2Inventory(object): for instance in reservation.instances: return instance - def add_instance(self, instance, region): ''' Adds an instance to the inventory and index, as long as it is addressable ''' @@ -345,25 +348,40 @@ class Ec2Inventory(object): # Inventory: Group by instance ID (always a group of 1) self.inventory[instance.id] = [dest] + if self.nested_groups: + self.push_group(self.inventory, 'instances', instance.id) # Inventory: Group by region - self.push(self.inventory, region, dest) + if self.nested_groups: + self.push_group(self.inventory, 'regions', region) + else: + self.push(self.inventory, region, dest) # Inventory: Group by availability zone self.push(self.inventory, instance.placement, dest) + if self.nested_groups: + self.push_group(self.inventory, region, instance.placement) # Inventory: Group by instance type - self.push(self.inventory, self.to_safe('type_' + instance.instance_type), dest) + type_name = self.to_safe('type_' + instance.instance_type) + self.push(self.inventory, type_name, dest) + if self.nested_groups: + self.push_group(self.inventory, 'types', type_name) # Inventory: Group by key pair if instance.key_name: - self.push(self.inventory, self.to_safe('key_' + instance.key_name), dest) + key_name = self.to_safe('key_' + instance.key_name) + self.push(self.inventory, key_name, dest) + if self.nested_groups: + self.push_group(self.inventory, 'keys', key_name) # Inventory: Group by security group try: for group in instance.groups: key = self.to_safe("security_group_" + group.name) self.push(self.inventory, key, dest) + if self.nested_groups: + self.push_group(self.inventory, 'security_groups', key) except AttributeError: print 'Package boto seems a bit older.' print 'Please upgrade boto >= 2.3.0.' @@ -373,12 +391,17 @@ class Ec2Inventory(object): for k, v in instance.tags.iteritems(): key = self.to_safe("tag_" + k + "=" + v) self.push(self.inventory, key, dest) + if self.nested_groups: + self.push_group(self.inventory, 'tags', self.to_safe("tag_" + k)) + self.push_group(self.inventory, self.to_safe("tag_" + k), key) # Inventory: Group by Route53 domain names if enabled if self.route53_enabled: route53_names = self.get_instance_route53_names(instance) for name in route53_names: self.push(self.inventory, name, dest) + if self.nested_groups: + self.push_group(self.inventory, 'route53', name) # Global Tag: tag all EC2 instances self.push(self.inventory, 'ec2', dest) @@ -410,21 +433,34 @@ class Ec2Inventory(object): # Inventory: Group by instance ID (always a group of 1) self.inventory[instance.id] = [dest] + if self.nested_groups: + self.push_group(self.inventory, 'instances', instance.id) # Inventory: Group by region - self.push(self.inventory, region, dest) + if self.nested_groups: + self.push_group(self.inventory, 'regions', region) + else: + self.push(self.inventory, region, dest) # Inventory: Group by availability zone self.push(self.inventory, instance.availability_zone, dest) + if self.nested_groups: + self.push_group(self.inventory, region, instance.availability_zone) # Inventory: Group by instance type - self.push(self.inventory, self.to_safe('type_' + instance.instance_class), dest) + type_name = self.to_safe('type_' + instance.instance_class) + self.push(self.inventory, type_name, dest) + if self.nested_groups: + self.push_group(self.inventory, 'types', type_name) # Inventory: Group by security group try: if instance.security_group: key = self.to_safe("security_group_" + instance.security_group.name) self.push(self.inventory, key, dest) + if self.nested_groups: + self.push_group(self.inventory, 'security_groups', key) + except AttributeError: print 'Package boto seems a bit older.' print 'Please upgrade boto >= 2.3.0.' @@ -432,13 +468,19 @@ class Ec2Inventory(object): # Inventory: Group by engine self.push(self.inventory, self.to_safe("rds_" + instance.engine), dest) + if self.nested_groups: + self.push_group(self.inventory, 'rds_engines', self.to_safe("rds_" + instance.engine)) # Inventory: Group by parameter group self.push(self.inventory, self.to_safe("rds_parameter_group_" + instance.parameter_group.name), dest) + if self.nested_groups: + self.push_group(self.inventory, 'rds_parameter_groups', self.to_safe("rds_parameter_group_" + instance.parameter_group.name)) # Global Tag: all RDS instances self.push(self.inventory, 'rds', dest) + self.inventory["_meta"]["hostvars"][dest] = self.get_host_info_dict_from_instance(instance) + def get_route53_records(self): ''' Get and store the map of resource records to domain names that @@ -553,14 +595,23 @@ class Ec2Inventory(object): return self.json_format_dict(self.get_host_info_dict_from_instance(instance), True) def push(self, my_dict, key, element): - ''' Pushed an element onto an array that may not have been defined in + ''' Push an element onto an array that may not have been defined in the dict ''' - - if key in my_dict: - my_dict[key].append(element); + group_info = my_dict.setdefault(key, []) + if isinstance(group_info, dict): + host_list = group_info.setdefault('hosts', []) + host_list.append(element) else: - my_dict[key] = [element] - + group_info.append(element) + + def push_group(self, my_dict, key, element): + ''' Push a group as a child of another group. ''' + parent_group = my_dict.setdefault(key, {}) + if not isinstance(parent_group, dict): + parent_group = my_dict[key] = {'hosts': parent_group} + child_groups = parent_group.setdefault('children', []) + if element not in child_groups: + child_groups.append(element) def get_inventory_from_cache(self): ''' Reads the inventory from the cache file and returns it as a JSON