From 6ce3ef36e3d2ac93441f73ddc84823c54ba1d4aa Mon Sep 17 00:00:00 2001 From: Herby Gillot Date: Mon, 10 Nov 2014 21:12:44 -0500 Subject: [PATCH] Use existing ec2_eip address if already associated If an EC2 instance is already associated with an EIP address, we use that, rather than allocating a new EIP address and associating it with that. Fixes #35. --- cloud/amazon/ec2_eip.py | 59 ++++++++++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 13 deletions(-) diff --git a/cloud/amazon/ec2_eip.py b/cloud/amazon/ec2_eip.py index 5f84ff24ff5..7258ea04759 100644 --- a/cloud/amazon/ec2_eip.py +++ b/cloud/amazon/ec2_eip.py @@ -74,6 +74,9 @@ EXAMPLES = ''' - name: output the IP debug: msg="Allocated IP is {{ eip.public_ip }}" +- name: another way of allocating an elastic IP without associating it to anything + ec2_eip: state='present' + - name: provision new instances with ec2 ec2: keypair=mykey instance_type=c1.medium image=ami-40603AD1 wait=yes group=webserver count=3 register: ec2 @@ -94,7 +97,6 @@ try: except ImportError: HAS_BOTO = False - wait_timeout = 0 def associate_ip_and_instance(ec2, address, instance_id, module): @@ -141,7 +143,7 @@ def disassociate_ip_and_instance(ec2, address, instance_id, module): module.fail_json(msg="disassociation failed") -def find_address(ec2, public_ip, module): +def find_address(ec2, public_ip, module, fail_on_not_found=True): """ Find an existing Elastic IP address """ if wait_timeout != 0: timeout = time.time() + wait_timeout @@ -151,7 +153,8 @@ def find_address(ec2, public_ip, module): break except boto.exception.EC2ResponseError, e: if "Address '%s' not found." % public_ip in e.message : - pass + if not fail_on_not_found: + return None else: module.fail_json(msg=str(e.message)) time.sleep(5) @@ -162,6 +165,9 @@ def find_address(ec2, public_ip, module): try: addresses = ec2.get_all_addresses([public_ip]) except boto.exception.EC2ResponseError, e: + if "Address '%s' not found." % public_ip in e.message : + if not fail_on_not_found: + return None module.fail_json(msg=str(e.message)) return addresses[0] @@ -175,6 +181,16 @@ def ip_is_associated_with_instance(ec2, public_ip, instance_id, module): else: return False +def instance_is_associated(ec2, instance, module): + """ + Check if the given instance object is already associated with an + elastic IP + """ + instance_ip = instance.ip_address + if not instance_ip: + return False + eip = find_address(ec2, instance_ip, module, fail_on_not_found=False) + return (eip and (eip.public_ip == instance_ip)) def allocate_address(ec2, domain, module, reuse_existing_ip_allowed): """ Allocate a new elastic IP address (when needed) and return it """ @@ -189,7 +205,7 @@ def allocate_address(ec2, domain, module, reuse_existing_ip_allowed): domain_filter = { 'domain' : 'standard' } all_addresses = ec2.get_all_addresses(filters=domain_filter) - unassociated_addresses = filter(lambda a: a.instance_id == "", all_addresses) + unassociated_addresses = filter(lambda a: not a.instance_id, all_addresses) if unassociated_addresses: address = unassociated_addresses[0]; else: @@ -232,6 +248,15 @@ def find_instance(ec2, instance_id, module): module.fail_json(msg="could not find instance" + instance_id) +def allocate_eip(ec2, eip_domain, module, reuse_existing_ip_allowed, new_eip_timeout): + # Allocate a new elastic IP + address = allocate_address(ec2, eip_domain, module, reuse_existing_ip_allowed) + # overriding the timeout since this is a a newly provisioned ip + global wait_timeout + wait_timeout = new_eip_timeout + return address + + def main(): argument_spec = ec2_argument_spec() argument_spec.update(dict( @@ -264,24 +289,32 @@ def main(): new_eip_timeout = int(module.params.get('wait_timeout')) if state == 'present': - # Allocate an EIP and exit - if not instance_id and not public_ip: - address = allocate_address(ec2, domain, module, reuse_existing_ip_allowed) + # If both instance_id and public_ip are not specified, allocate a new + # elastic IP, and exit. + if not instance_id and not public_ip: + address = allocate_eip(ec2, domain, module, + reuse_existing_ip_allowed, new_eip_timeout) module.exit_json(changed=True, public_ip=address.public_ip) # Return the EIP object since we've been given a public IP if public_ip: address = find_address(ec2, public_ip, module) - # Allocate an IP for instance since no public_ip was provided - if instance_id and not public_ip: + if instance_id and not public_ip: instance = find_instance(ec2, instance_id, module) + if instance.vpc_id: domain = "vpc" - address = allocate_address(ec2, domain, module, reuse_existing_ip_allowed) - # overriding the timeout since this is a a newly provisioned ip - global wait_timeout - wait_timeout = new_eip_timeout + + # Do nothing if the instance is already associated with an + # elastic IP. + if instance_is_associated(ec2, instance, module): + module.exit_json(changed=False, public_ip=instance.ip_address) + + # If the instance is not already associated with an elastic IP, + # allocate a new one. + address = allocate_eip( + ec2, domain, module, reuse_existing_ip_allowed, new_eip_timeout) # Associate address object (provided or allocated) with instance associate_ip_and_instance(ec2, address, instance_id, module)