From 89f8e249fa7a2975d49ae5cdd86c853d734f6a2e Mon Sep 17 00:00:00 2001 From: whiter Date: Tue, 13 Oct 2015 17:32:27 +1100 Subject: [PATCH] Refactor ec2_remote_facts to use filters --- cloud/amazon/ec2_remote_facts.py | 204 ++++++++++++++++--------------- 1 file changed, 108 insertions(+), 96 deletions(-) diff --git a/cloud/amazon/ec2_remote_facts.py b/cloud/amazon/ec2_remote_facts.py index f273f17a8ce..cb92ccba74d 100644 --- a/cloud/amazon/ec2_remote_facts.py +++ b/cloud/amazon/ec2_remote_facts.py @@ -16,137 +16,149 @@ DOCUMENTATION = ''' --- module: ec2_remote_facts -short_description: ask EC2 for information about other instances. +short_description: Gather facts about ec2 instances in AWS description: - - Only supports search for hostname by tags currently. Looking to add more later. + - Gather facts about ec2 instances in AWS version_added: "2.0" options: - key: + filters: description: - - instance tag key in EC2 - required: false - default: Name - value: - description: - - instance tag value in EC2 + - A dict of filters to apply. Each dict item consists of a filter key and a filter value. See U(http://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeInstances.html) for possible filters. required: false default: null - lookup: - description: - - What type of lookup to use when searching EC2 instance info. - required: false - default: tags - region: - description: - - EC2 region that it should look for tags in - required: false - default: All Regions - ignore_state: - description: - - instance state that should be ignored such as terminated. - required: false - default: terminated author: - "Michael Schuett (@michaeljs1990)" -extends_documentation_fragment: aws +extends_documentation_fragment: + - aws + - ec2 ''' EXAMPLES = ''' # Note: These examples do not set authentication details, see the AWS Guide for details. -# Basic provisioning example +# Gather facts about all ec2 instances - ec2_remote_facts: - key: mykey - value: myvalue - register: servers + +# Gather facts about all running ec2 instances with a tag of Name:Example +- ec2_remote_facts: + filters: + instance-state-name: running + "tag:Name": Example + +# Gather facts about instance i-123456 +- ec2_remote_facts: + filters: + instance-id: i-123456 + +# Gather facts about all instances in vpc-123456 that are t2.small type +- ec2_remote_facts: + filters: + vpc-id: vpc-123456 + instance-type: t2.small + ''' + try: - import boto import boto.ec2 + from boto.exception import BotoServerError HAS_BOTO = True except ImportError: HAS_BOTO = False -def todict(obj, classkey=None): - if isinstance(obj, dict): - data = {} - for (k, v) in obj.items(): - data[k] = todict(v, classkey) - return data - elif hasattr(obj, "_ast"): - return todict(obj._ast()) - elif hasattr(obj, "__iter__"): - return [todict(v, classkey) for v in obj] - elif hasattr(obj, "__dict__"): - # This Class causes a recursive loop and at this time is not worth - # debugging. If it's useful later I'll look into it. - if not isinstance(obj, boto.ec2.blockdevicemapping.BlockDeviceType): - data = dict([(key, todict(value, classkey)) - for key, value in obj.__dict__.iteritems() - if not callable(value) and not key.startswith('_')]) - if classkey is not None and hasattr(obj, "__class__"): - data[classkey] = obj.__class__.__name__ - return data - else: - return obj +def get_instance_info(instance): + + # Get groups + groups = [] + for group in instance.groups: + groups.append({ 'id': group.id, 'name': group.name }.copy()) -def get_all_ec2_regions(module): + # Get interfaces + interfaces = [] + for interface in instance.interfaces: + interfaces.append({ 'id': interface.id, 'mac_address': interface.mac_address }.copy()) + + instance_info = { 'id': instance.id, + 'kernel': instance.kernel, + 'instance_profile': instance.instance_profile, + 'root_device_type': instance.root_device_type, + 'private_dns_name': instance.private_dns_name, + 'public_dns_name': instance.public_dns_name, + 'ebs_optimized': instance.ebs_optimized, + 'client_token': instance.client_token, + 'virtualization_type': instance.virtualization_type, + 'architecture': instance.architecture, + 'ramdisk': instance.ramdisk, + 'tags': instance.tags, + 'key_name': instance.key_name, + 'source_destination_check': instance.sourceDestCheck, + 'image_id': instance.image_id, + 'groups': groups, + 'interfaces': interfaces, + 'spot_instance_request_id': instance.spot_instance_request_id, + 'requester_id': instance.requester_id, + 'monitoring_state': instance.monitoring_state, + 'placement': { + 'tenancy': instance._placement.tenancy, + 'zone': instance._placement.zone + }, + 'ami_launch_index': instance.ami_launch_index, + 'launch_time': instance.launch_time, + 'hypervisor': instance.hypervisor, + 'region': instance.region.name, + 'persistent': instance.persistent, + 'private_ip_address': instance.private_ip_address, + 'state': instance._state.name, + 'vpc_id': instance.vpc_id, + } + + return instance_info + + +def list_ec2_instances(connection, module): + + filters = module.params.get("filters") + instance_dict_array = [] + try: - regions = boto.ec2.regions() - except Exception, e: - module.fail_json('Boto authentication issue: %s' % e) - - return regions - -# Connect to ec2 region -def connect_to_region(region, module): - try: - conn = boto.ec2.connect_to_region(region.name) - except Exception, e: - print module.jsonify('error connecting to region: ' + region.name) - conn = None - # connect_to_region will fail "silently" by returning - # None if the region name is wrong or not supported - return conn + all_instances = connection.get_only_instances(filters=filters) + except BotoServerError as e: + module.fail_json(msg=e.message) + + for instance in all_instances: + instance_dict_array.append(get_instance_info(instance)) + + module.exit_json(instances=instance_dict_array) + def main(): - module = AnsibleModule( - argument_spec = dict( - key = dict(default='Name'), - value = dict(), - lookup = dict(default='tags'), - ignore_state = dict(default='terminated'), - region = dict(), + argument_spec = ec2_argument_spec() + argument_spec.update( + dict( + filters = dict(default=None, type='dict') ) ) + module = AnsibleModule(argument_spec=argument_spec) + if not HAS_BOTO: module.fail_json(msg='boto required for this module') - server_info = list() + region, ec2_url, aws_connect_params = get_aws_connection_info(module) - for region in get_all_ec2_regions(module): - conn = connect_to_region(region, module) + if region: try: - # Run when looking up by tag names, only returning hostname currently - if module.params.get('lookup') == 'tags': - ec2_key = 'tag:' + module.params.get('key') - ec2_value = module.params.get('value') - reservations = conn.get_all_instances(filters={ec2_key : ec2_value}) - for instance in [i for r in reservations for i in r.instances]: - if instance.private_ip_address != None: - instance.hostname = 'ip-' + instance.private_ip_address.replace('.', '-') - if instance._state.name not in module.params.get('ignore_state'): - server_info.append(todict(instance)) - except: - print module.jsonify('error getting instances from: ' + region.name) - - ec2_facts_result = dict(changed=True, ec2=server_info) - - module.exit_json(**ec2_facts_result) + connection = connect_to_aws(boto.ec2, region, **aws_connect_params) + except (boto.exception.NoAuthHandlerFound, StandardError), e: + module.fail_json(msg=str(e)) + else: + module.fail_json(msg="region must be specified") + + list_ec2_instances(connection, module) # import module snippets from ansible.module_utils.basic import * from ansible.module_utils.ec2 import * -main() +if __name__ == '__main__': + main() +