From c8cc089de21ba1429bf7f5bfabec6574669886b7 Mon Sep 17 00:00:00 2001 From: James Martin Date: Tue, 29 Jul 2014 00:59:11 -0600 Subject: [PATCH 1/2] Fixes a regression in 1453f7b013393678b8a616d7cfbda1b68b2c31db that would causes unecessary wait_timeouts in certain module operations when a given IP address could not be found. Now wait_timeouts are only active during address associations of newly provisioned EIPs. Cleanup and comment of main(). s Added wait timeout. --- library/cloud/ec2_eip | 84 +++++++++++++++++++++++++++---------------- 1 file changed, 53 insertions(+), 31 deletions(-) diff --git a/library/cloud/ec2_eip b/library/cloud/ec2_eip index 92e249b66c..73efbe662d 100644 --- a/library/cloud/ec2_eip +++ b/library/cloud/ec2_eip @@ -41,6 +41,11 @@ options: required: false default: false version_added: "1.6" + wait_timeout: + description: + - how long to wait in seconds for newly provisioned EIPs to become available + default: 300 + version_added: "1.7" extends_documentation_fragment: aws author: Lorin Hochstein @@ -91,6 +96,8 @@ else: boto_found = True +wait_timeout = 0 + def associate_ip_and_instance(ec2, address, instance_id, module): if ip_is_associated_with_instance(ec2, address.public_ip, instance_id, module): module.exit_json(changed=False, public_ip=address.public_ip) @@ -136,24 +143,27 @@ def disassociate_ip_and_instance(ec2, address, instance_id, module): def find_address(ec2, public_ip, module): - """ Find an existing Elastic IP address """ - - wait_timeout = 600 - wait_timeout = time.time() + wait_timeout - - while wait_timeout > time.time(): + """ Find an existing Elastic IP address """ + if wait_timeout != 0: + timeout = time.time() + wait_timeout + while timeout > time.time(): + try: + addresses = ec2.get_all_addresses([public_ip]) + break + except boto.exception.EC2ResponseError, e: + if "Address '%s' not found." % public_ip in e.message : + pass + else: + module.fail_json(msg=str(e.message)) + time.sleep(5) + + if timeout <= time.time(): + module.fail_json(msg = "wait for EIPs timeout on %s" % time.asctime()) + else: try: addresses = ec2.get_all_addresses([public_ip]) - break except boto.exception.EC2ResponseError, e: - if "Address '%s' not found." % public_ip in e.message : - pass - else: - module.fail_json(msg=str(e.message)) - time.sleep(5) - - if wait_timeout <= time.time(): - module.fail_json(msg = "wait for EIPs timeout on %s" % time.asctime()) + module.fail_json(msg=str(e.message)) return addresses[0] @@ -232,6 +242,7 @@ def main(): choices=['present', 'absent']), in_vpc = dict(required=False, type='bool', default=False), reuse_existing_ip_allowed = dict(required=False, type='bool', default=False), + wait_timeout = dict(default=300), ) ) @@ -250,29 +261,40 @@ def main(): state = module.params.get('state') in_vpc = module.params.get('in_vpc') domain = "vpc" if in_vpc else None - reuse_existing_ip_allowed = module.params.get('reuse_existing_ip_allowed'); + reuse_existing_ip_allowed = module.params.get('reuse_existing_ip_allowed') + new_eip_timeout = int(module.params.get('wait_timeout')) if state == 'present': - if public_ip is None: - if instance_id is None: - address = allocate_address(ec2, domain, module, reuse_existing_ip_allowed) - module.exit_json(changed=True, public_ip=address.public_ip) - else: - # Determine if the instance is inside a VPC or not - instance = find_instance(ec2, instance_id, module) - if instance.vpc_id != None: - domain = "vpc" - address = allocate_address(ec2, domain, module, reuse_existing_ip_allowed) - else: + # Allocate an EIP and exit + if not instance_id and not public_ip: + address = allocate_address(ec2, domain, module, reuse_existing_ip_allowed) + 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: + 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 + + # Associate address object (provided or allocated) with instance associate_ip_and_instance(ec2, address, instance_id, module) + else: - if instance_id is None: - release_address(ec2, public_ip, module) - else: + #disassociating address from instance + if instance_id: address = find_address(ec2, public_ip, module) disassociate_ip_and_instance(ec2, address, instance_id, module) - + #releasing address + else: + release_address(ec2, public_ip, module) # import module snippets From 5c2c10987e91f698aa614e557a7fde01e1697329 Mon Sep 17 00:00:00 2001 From: James Cammarata Date: Tue, 5 Aug 2014 09:20:23 -0500 Subject: [PATCH 2/2] Re-enable the ec2_eip test --- test/integration/amazon.yml | 2 +- .../roles/test_ec2_eip/tasks/main.yml | 65 +++++++++++-------- 2 files changed, 39 insertions(+), 28 deletions(-) diff --git a/test/integration/amazon.yml b/test/integration/amazon.yml index 25abbd0870..0ccebebf7e 100644 --- a/test/integration/amazon.yml +++ b/test/integration/amazon.yml @@ -8,7 +8,7 @@ #- { role: test_ec2_tag, tags: test_ec2_tag } #- { role: test_ec2_facts, tags: test_ec2_facts } - { role: test_ec2_elb_lb, tags: test_ec2_elb_lb } - #- { role: test_ec2_eip, tags: test_ec2_eip } + - { role: test_ec2_eip, tags: test_ec2_eip } #- { role: test_ec2_ami, tags: test_ec2_ami } #- { role: test_ec2, tags: test_ec2 } - { role: test_ec2_asg, tags: test_ec2_asg } diff --git a/test/integration/roles/test_ec2_eip/tasks/main.yml b/test/integration/roles/test_ec2_eip/tasks/main.yml index 0223fe74bd..9f17f557e3 100644 --- a/test/integration/roles/test_ec2_eip/tasks/main.yml +++ b/test/integration/roles/test_ec2_eip/tasks/main.yml @@ -32,17 +32,28 @@ # test credential parameters # ============================================================ +# create a keypair using the ssh key +- name: create the keypair for ec2 + ec2_key: + name: "{{ resource_prefix }}" + region: "{{ ec2_region }}" + ec2_access_key: "{{ ec2_access_key }}" + ec2_secret_key: "{{ ec2_secret_key }}" + key_material: "{{ key_material }}" + wait: yes + state: present + +# ============================================================ # create test instance - - name: create test instance local_action: module: ec2 - key_name: "{{ eip_ec2_keyname }}" + key_name: "{{ resource_prefix }}" region: "{{ ec2_region }}" instance_type: t1.micro - image: "{{ eip_ec2_image }}" + image: ami-fb8e9292 wait: yes instance_tags: Name: "{{ tag_prefix }}" @@ -243,32 +254,32 @@ # ============================================================ # re-use existing VPC EIP with instance - -- name: re-use existing EIP with instance - ec2_eip: - in_vpc=yes - reuse_existing_ip_allowed=yes - instance_id={{ instance_id }} - ec2_access_key={{ ec2_access_key }} - ec2_secret_key={{ ec2_secret_key }} - region={{ ec2_region }} - register: result - -- name: assert new EIP was assigned - assert: - that: - - '"failed" not in result' - - '"public_ip" in result' +# DISABLED BY JCAMMARATA +#- name: re-use existing VPC EIP with instance +# ec2_eip: +# in_vpc=yes +# reuse_existing_ip_allowed=yes +# instance_id={{ instance_id }} +# ec2_access_key={{ ec2_access_key }} +# ec2_secret_key={{ ec2_secret_key }} +# region={{ ec2_region }} +# register: result +# +#- name: assert new VPC EIP was assigned +# assert: +# that: +# - '"failed" not in result' +# - '"public_ip" in result' +# +# +#- name: disassociate VPC EIP associated with instance +# ec2_eip: state=absent public_ip={{ eip }} region={{ ec2_region }} +# ec2_access_key={{ ec2_access_key }} +# ec2_secret_key={{ ec2_secret_key }} +# instance_id={{ instance_id }} -- name: disassociate EIP associated with instance - ec2_eip: state=absent public_ip={{ eip }} region={{ ec2_region }} - ec2_access_key={{ ec2_access_key }} - ec2_secret_key={{ ec2_secret_key }} - instance_id={{ instance_id }} - - -- name: deactivate EIP +- name: deactivate VPC EIP ec2_eip: state=absent public_ip={{ eip }} region={{ ec2_region }} ec2_access_key={{ ec2_access_key }} ec2_secret_key={{ ec2_secret_key }}