From 7f8654d586be176a355a685c249eb28fd5a17d0d Mon Sep 17 00:00:00 2001 From: Ryan Brown Date: Fri, 1 Jun 2018 18:45:22 -0400 Subject: [PATCH] Add minimum botocore and boto3 checking to AnsibleAWSModule (#41005) * Add minimum botocore and boto3 checking to AnsibleAWSModule --- lib/ansible/module_utils/aws/core.py | 28 ++++++++++++++++++- .../modules/cloud/amazon/ec2_vpc_subnet.py | 5 ++-- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/lib/ansible/module_utils/aws/core.py b/lib/ansible/module_utils/aws/core.py index e4199fb14a0..41d4e15f6a9 100644 --- a/lib/ansible/module_utils/aws/core.py +++ b/lib/ansible/module_utils/aws/core.py @@ -60,11 +60,13 @@ don't need to be wrapped in the backoff decorator. """ +import traceback from functools import wraps +from distutils.version import LooseVersion + from ansible.module_utils.basic import AnsibleModule from ansible.module_utils._text import to_native from ansible.module_utils.ec2 import HAS_BOTO3, camel_dict_to_snake_dict, ec2_argument_spec, boto3_conn, get_aws_connection_info -import traceback # We will also export HAS_BOTO3 so end user modules can use it. __all__ = ('AnsibleAWSModule', 'HAS_BOTO3',) @@ -193,6 +195,30 @@ class AnsibleAWSModule(object): return dict(boto3_version=boto3.__version__, botocore_version=botocore.__version__) + def boto3_at_least(self, desired): + """Check if the available boto3 version is greater than or equal to a desired version. + + Usage: + if module.params.get('assign_ipv6_address') and not module.boto3_at_least('1.4.4'): + # conditionally fail on old boto3 versions if a specific feature is not supported + module.fail_json(msg="Boto3 can't deal with EC2 IPv6 addresses before version 1.4.4.") + """ + existing = self._gather_versions() + return LooseVersion(existing['boto3_version']) >= LooseVersion(desired) + + def botocore_at_least(self, desired): + """Check if the available botocore version is greater than or equal to a desired version. + + Usage: + if not module.botocore_at_least('1.2.3'): + module.fail_json(msg='The Serverless Elastic Load Compute Service is not in botocore before v1.2.3') + if not module.botocore_at_least('1.5.3'): + module.warn('Botocore did not include waiters for Service X before 1.5.3. ' + 'To wait until Service X resources are fully available, update botocore.') + """ + existing = self._gather_versions() + return LooseVersion(existing['botocore_version']) >= LooseVersion(desired) + class _RetryingBotoClientWrapper(object): __never_wait = ( diff --git a/lib/ansible/modules/cloud/amazon/ec2_vpc_subnet.py b/lib/ansible/modules/cloud/amazon/ec2_vpc_subnet.py index 891c41ccfef..19a4a50ded7 100644 --- a/lib/ansible/modules/cloud/amazon/ec2_vpc_subnet.py +++ b/lib/ansible/modules/cloud/amazon/ec2_vpc_subnet.py @@ -205,7 +205,6 @@ subnet: import time import traceback -from distutils.version import LooseVersion try: import botocore @@ -255,7 +254,7 @@ def describe_subnets_with_backoff(client, **params): def waiter_params(module, params, start_time): - if LooseVersion(botocore.__version__) >= "1.7.0": + if not module.botocore_at_least("1.7.0"): remaining_wait_timeout = int(module.params['wait_timeout'] + start_time - time.time()) params['WaiterConfig'] = {'Delay': 5, 'MaxAttempts': remaining_wait_timeout // 5} return params @@ -578,7 +577,7 @@ def main(): if module.params.get('assign_instances_ipv6') and not module.params.get('ipv6_cidr'): module.fail_json(msg="assign_instances_ipv6 is True but ipv6_cidr is None or an empty string") - if LooseVersion(botocore.__version__) < "1.7.0": + if not module.botocore_at_least("1.7.0"): module.warn("botocore >= 1.7.0 is required to use wait_timeout for custom wait times") region, ec2_url, aws_connect_params = get_aws_connection_info(module, boto3=True)