mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
Increase delay and tries for ec2_vpc_net backoff - fixes #36063, fixes #37323, fixes #36078 (#37354)
* Increase delay and tries for ec2_vpc_net backoff Wait for DHCP option to be created in ec2_vpc_dhcp_option Wait for all modifications to the VPC * Use the vpc_available waiter because is uses Filters * Missed one * Optimize retries to only occur if the functionality is available * Increase max wait time * Add comments to explain what the waiters are doing
This commit is contained in:
parent
cdd21e2170
commit
16f8a993a0
2 changed files with 91 additions and 14 deletions
|
@ -187,7 +187,7 @@ EXAMPLES = """
|
||||||
|
|
||||||
import collections
|
import collections
|
||||||
import traceback
|
import traceback
|
||||||
|
from time import sleep, time
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
from ansible.module_utils.ec2 import HAS_BOTO, connect_to_aws, ec2_argument_spec, get_aws_connection_info
|
from ansible.module_utils.ec2 import HAS_BOTO, connect_to_aws, ec2_argument_spec, get_aws_connection_info
|
||||||
|
|
||||||
|
@ -359,6 +359,23 @@ def main():
|
||||||
new_options['ntp-servers'],
|
new_options['ntp-servers'],
|
||||||
new_options['netbios-name-servers'],
|
new_options['netbios-name-servers'],
|
||||||
new_options['netbios-node-type'])
|
new_options['netbios-node-type'])
|
||||||
|
|
||||||
|
# wait for dhcp option to be accessible
|
||||||
|
found_dhcp_opt = False
|
||||||
|
start_time = time()
|
||||||
|
while time() < start_time + 300:
|
||||||
|
try:
|
||||||
|
found_dhcp_opt = connection.get_all_dhcp_options(dhcp_options_ids=[dhcp_option.id])
|
||||||
|
except EC2ResponseError as e:
|
||||||
|
if e.error_code == 'InvalidDhcpOptionID.NotFound':
|
||||||
|
sleep(3)
|
||||||
|
else:
|
||||||
|
module.fail_json(msg="Failed to describe DHCP options", exception=traceback.format_exc)
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
if not found_dhcp_opt:
|
||||||
|
module.fail_json(msg="Failed to wait for {0} to be available.".format(dhcp_option.id))
|
||||||
|
|
||||||
changed = True
|
changed = True
|
||||||
if params['tags']:
|
if params['tags']:
|
||||||
ensure_tags(module, connection, dhcp_option.id, params['tags'], False, module.check_mode)
|
ensure_tags(module, connection, dhcp_option.id, params['tags'], False, module.check_mode)
|
||||||
|
|
|
@ -164,6 +164,7 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass # Handled by AnsibleAWSModule
|
pass # Handled by AnsibleAWSModule
|
||||||
|
|
||||||
|
from time import sleep, time
|
||||||
from ansible.module_utils.aws.core import AnsibleAWSModule
|
from ansible.module_utils.aws.core import AnsibleAWSModule
|
||||||
from ansible.module_utils.ec2 import (boto3_conn, get_aws_connection_info, ec2_argument_spec, camel_dict_to_snake_dict,
|
from ansible.module_utils.ec2 import (boto3_conn, get_aws_connection_info, ec2_argument_spec, camel_dict_to_snake_dict,
|
||||||
ansible_dict_to_boto3_tag_list, boto3_tag_list_to_ansible_dict, AWSRetry)
|
ansible_dict_to_boto3_tag_list, boto3_tag_list_to_ansible_dict, AWSRetry)
|
||||||
|
@ -194,23 +195,34 @@ def vpc_exists(module, vpc, name, cidr_block, multi):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
@AWSRetry.backoff(delay=3, tries=8, catch_extra_error_codes=['InvalidVpcID.NotFound'])
|
||||||
|
def get_classic_link_with_backoff(connection, vpc_id):
|
||||||
|
try:
|
||||||
|
return connection.describe_vpc_classic_link(VpcIds=[vpc_id])['Vpcs'][0].get('ClassicLinkEnabled')
|
||||||
|
except botocore.exceptions.ClientError as e:
|
||||||
|
if e.response["Error"]["Message"] == "The functionality you requested is not available in this region.":
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
def get_vpc(module, connection, vpc_id):
|
def get_vpc(module, connection, vpc_id):
|
||||||
|
# wait for vpc to be available
|
||||||
|
try:
|
||||||
|
connection.get_waiter('vpc_available').wait(VpcIds=[vpc_id])
|
||||||
|
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
|
||||||
|
module.fail_json_aws(e, msg="Unable to wait for VPC {0} to be available.".format(vpc_id))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
vpc_obj = AWSRetry.backoff(
|
vpc_obj = AWSRetry.backoff(
|
||||||
delay=1, tries=5,
|
delay=3, tries=8,
|
||||||
catch_extra_error_codes=['InvalidVpcID.NotFound'],
|
catch_extra_error_codes=['InvalidVpcID.NotFound'],
|
||||||
)(connection.describe_vpcs)(VpcIds=[vpc_id])['Vpcs'][0]
|
)(connection.describe_vpcs)(VpcIds=[vpc_id])['Vpcs'][0]
|
||||||
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
|
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
|
||||||
module.fail_json_aws(e, msg="Failed to describe VPCs")
|
module.fail_json_aws(e, msg="Failed to describe VPCs")
|
||||||
try:
|
try:
|
||||||
classic_link = connection.describe_vpc_classic_link(VpcIds=[vpc_id])['Vpcs'][0].get('ClassicLinkEnabled')
|
vpc_obj['ClassicLinkEnabled'] = get_classic_link_with_backoff(connection, vpc_id)
|
||||||
vpc_obj['ClassicLinkEnabled'] = classic_link
|
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
|
||||||
except botocore.exceptions.ClientError as e:
|
|
||||||
if e.response["Error"]["Message"] == "The functionality you requested is not available in this region.":
|
|
||||||
vpc_obj['ClassicLinkEnabled'] = False
|
|
||||||
else:
|
|
||||||
module.fail_json_aws(e, msg="Failed to describe VPCs")
|
|
||||||
except botocore.exceptions.BotoCoreError as e:
|
|
||||||
module.fail_json_aws(e, msg="Failed to describe VPCs")
|
module.fail_json_aws(e, msg="Failed to describe VPCs")
|
||||||
|
|
||||||
return vpc_obj
|
return vpc_obj
|
||||||
|
@ -231,6 +243,12 @@ def update_vpc_tags(connection, module, vpc_id, tags, name):
|
||||||
delay=1, tries=5,
|
delay=1, tries=5,
|
||||||
catch_extra_error_codes=['InvalidVpcID.NotFound'],
|
catch_extra_error_codes=['InvalidVpcID.NotFound'],
|
||||||
)(connection.create_tags)(Resources=[vpc_id], Tags=tags)
|
)(connection.create_tags)(Resources=[vpc_id], Tags=tags)
|
||||||
|
|
||||||
|
# Wait for tags to be updated
|
||||||
|
expected_tags = boto3_tag_list_to_ansible_dict(tags)
|
||||||
|
filters = [{'Name': 'tag:{0}'.format(key), 'Values': [value]} for key, value in expected_tags.items()]
|
||||||
|
connection.get_waiter('vpc_available').wait(VpcIds=[vpc_id], Filters=filters)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
@ -245,6 +263,14 @@ def update_dhcp_opts(connection, module, vpc_obj, dhcp_id):
|
||||||
connection.associate_dhcp_options(DhcpOptionsId=dhcp_id, VpcId=vpc_obj['VpcId'])
|
connection.associate_dhcp_options(DhcpOptionsId=dhcp_id, VpcId=vpc_obj['VpcId'])
|
||||||
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
|
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
|
||||||
module.fail_json_aws(e, msg="Failed to associate DhcpOptionsId {0}".format(dhcp_id))
|
module.fail_json_aws(e, msg="Failed to associate DhcpOptionsId {0}".format(dhcp_id))
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Wait for DhcpOptionsId to be updated
|
||||||
|
filters = [{'Name': 'dhcp-options-id', 'Values': [dhcp_id]}]
|
||||||
|
connection.get_waiter('vpc_available').wait(VpcIds=[vpc_obj['VpcId']], Filters=filters)
|
||||||
|
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
|
||||||
|
module.fail_json(msg="Failed to wait for DhcpOptionsId to be updated")
|
||||||
|
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
@ -258,9 +284,33 @@ def create_vpc(connection, module, cidr_block, tenancy):
|
||||||
module.exit_json(changed=True)
|
module.exit_json(changed=True)
|
||||||
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
|
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
|
||||||
module.fail_json_aws(e, "Failed to create the VPC")
|
module.fail_json_aws(e, "Failed to create the VPC")
|
||||||
|
|
||||||
|
# wait for vpc to exist
|
||||||
|
try:
|
||||||
|
connection.get_waiter('vpc_exists').wait(VpcIds=[vpc_obj['Vpc']['VpcId']])
|
||||||
|
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
|
||||||
|
module.fail_json_aws(e, msg="Unable to wait for VPC {0} to be created.".format(vpc_obj['Vpc']['VpcId']))
|
||||||
|
|
||||||
return vpc_obj['Vpc']['VpcId']
|
return vpc_obj['Vpc']['VpcId']
|
||||||
|
|
||||||
|
|
||||||
|
def wait_for_vpc_attribute(connection, module, vpc_id, attribute, expected_value):
|
||||||
|
start_time = time()
|
||||||
|
updated = False
|
||||||
|
while time() < start_time + 300:
|
||||||
|
current_value = connection.describe_vpc_attribute(
|
||||||
|
Attribute=attribute,
|
||||||
|
VpcId=vpc_id
|
||||||
|
)['{0}{1}'.format(attribute[0].upper(), attribute[1:])]['Value']
|
||||||
|
if current_value != expected_value:
|
||||||
|
sleep(3)
|
||||||
|
else:
|
||||||
|
updated = True
|
||||||
|
break
|
||||||
|
if not updated:
|
||||||
|
module.fail_json(msg="Failed to wait for {0} to be updated".format(attribute))
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
argument_spec = ec2_argument_spec()
|
argument_spec = ec2_argument_spec()
|
||||||
argument_spec.update(dict(
|
argument_spec.update(dict(
|
||||||
|
@ -316,6 +366,7 @@ def main():
|
||||||
if cidr['CidrBlockState']['State'] != 'disassociated')
|
if cidr['CidrBlockState']['State'] != 'disassociated')
|
||||||
to_add = [cidr for cidr in cidr_block if cidr not in associated_cidrs]
|
to_add = [cidr for cidr in cidr_block if cidr not in associated_cidrs]
|
||||||
to_remove = [associated_cidrs[cidr] for cidr in associated_cidrs if cidr not in cidr_block]
|
to_remove = [associated_cidrs[cidr] for cidr in associated_cidrs if cidr not in cidr_block]
|
||||||
|
expected_cidrs = [cidr for cidr in associated_cidrs if associated_cidrs[cidr] not in to_remove] + to_add
|
||||||
|
|
||||||
if len(cidr_block) > 1:
|
if len(cidr_block) > 1:
|
||||||
for cidr in to_add:
|
for cidr in to_add:
|
||||||
|
@ -362,10 +413,19 @@ def main():
|
||||||
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
|
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
|
||||||
module.fail_json_aws(e, "Failed to update enabled dns hostnames attribute")
|
module.fail_json_aws(e, "Failed to update enabled dns hostnames attribute")
|
||||||
|
|
||||||
try:
|
# wait for associated cidrs to match
|
||||||
connection.get_waiter('vpc_available').wait(VpcIds=[vpc_id])
|
if to_add or to_remove:
|
||||||
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
|
try:
|
||||||
module.fail_json_aws(e, msg="Unable to wait for VPC {0} to be available.".format(vpc_id))
|
connection.get_waiter('vpc_available').wait(
|
||||||
|
VpcIds=[vpc_id],
|
||||||
|
Filters=[{'Name': 'cidr-block-association.cidr-block', 'Values': expected_cidrs}]
|
||||||
|
)
|
||||||
|
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
|
||||||
|
module.fail_json_aws(e, "Failed to wait for CIDRs to update")
|
||||||
|
|
||||||
|
# try to wait for enableDnsSupport and enableDnsHostnames to match
|
||||||
|
wait_for_vpc_attribute(connection, module, vpc_id, 'enableDnsSupport', dns_support)
|
||||||
|
wait_for_vpc_attribute(connection, module, vpc_id, 'enableDnsHostnames', dns_hostnames)
|
||||||
|
|
||||||
final_state = camel_dict_to_snake_dict(get_vpc(module, connection, vpc_id))
|
final_state = camel_dict_to_snake_dict(get_vpc(module, connection, vpc_id))
|
||||||
final_state['tags'] = boto3_tag_list_to_ansible_dict(final_state.get('tags', []))
|
final_state['tags'] = boto3_tag_list_to_ansible_dict(final_state.get('tags', []))
|
||||||
|
|
Loading…
Reference in a new issue