2014-03-10 22:06:52 +01:00
# This code is part of Ansible, but is an independent component.
# This particular file snippet, and this file snippet only, is BSD licensed.
# Modules you write using this snippet, which is embedded dynamically by Ansible
# still belong to the author of the module, and may assign their own license
# to the complete work.
#
# Copyright (c), Michael DeHaan <michael.dehaan@gmail.com>, 2012-2013
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2015-09-18 14:04:58 +02:00
import os
try :
import boto3
2015-11-14 02:19:09 +01:00
import botocore
2015-09-18 14:04:58 +02:00
HAS_BOTO3 = True
except :
HAS_BOTO3 = False
2014-03-10 22:06:52 +01:00
2014-02-13 19:12:08 +01:00
try :
from distutils . version import LooseVersion
HAS_LOOSE_VERSION = True
except :
HAS_LOOSE_VERSION = False
2013-12-17 03:04:12 +01:00
2015-12-01 04:03:07 +01:00
class AnsibleAWSError ( Exception ) :
pass
2015-07-14 23:30:51 +02:00
def boto3_conn ( module , conn_type = None , resource = None , region = None , endpoint = None , * * params ) :
2015-11-06 00:15:45 +01:00
profile = params . pop ( ' profile_name ' , None )
params [ ' aws_session_token ' ] = params . pop ( ' security_token ' , None )
params [ ' verify ' ] = params . pop ( ' validate_certs ' , None )
2015-07-14 23:30:51 +02:00
if conn_type not in [ ' both ' , ' resource ' , ' client ' ] :
module . fail_json ( msg = ' There is an issue in the code of the module. You must specify either both, resource or client to the conn_type parameter in the boto3_conn function call ' )
if conn_type == ' resource ' :
2015-11-06 00:15:45 +01:00
resource = boto3 . session . Session ( profile_name = profile ) . resource ( resource , region_name = region , endpoint_url = endpoint , * * params )
2015-07-14 23:30:51 +02:00
return resource
elif conn_type == ' client ' :
2015-11-06 00:15:45 +01:00
client = boto3 . session . Session ( profile_name = profile ) . client ( resource , region_name = region , endpoint_url = endpoint , * * params )
2015-07-14 23:30:51 +02:00
return client
else :
2015-11-06 00:15:45 +01:00
resource = boto3 . session . Session ( profile_name = profile ) . resource ( resource , region_name = region , endpoint_url = endpoint , * * params )
client = boto3 . session . Session ( profile_name = profile ) . client ( resource , region_name = region , endpoint_url = endpoint , * * params )
2015-07-14 23:30:51 +02:00
return client , resource
2013-12-17 03:04:12 +01:00
2015-11-06 00:15:45 +01:00
2014-02-05 12:11:06 +01:00
def aws_common_argument_spec ( ) :
2014-02-05 12:11:06 +01:00
return dict (
2014-02-05 12:11:06 +01:00
ec2_url = dict ( ) ,
2014-02-09 00:35:26 +01:00
aws_secret_key = dict ( aliases = [ ' ec2_secret_key ' , ' secret_key ' ] , no_log = True ) ,
aws_access_key = dict ( aliases = [ ' ec2_access_key ' , ' access_key ' ] ) ,
2014-02-05 12:11:06 +01:00
validate_certs = dict ( default = True , type = ' bool ' ) ,
2014-12-25 21:31:34 +01:00
security_token = dict ( aliases = [ ' access_token ' ] , no_log = True ) ,
2014-02-05 12:11:06 +01:00
profile = dict ( ) ,
2014-02-09 00:35:26 +01:00
)
def ec2_argument_spec ( ) :
2014-02-05 12:11:06 +01:00
spec = aws_common_argument_spec ( )
2014-02-09 00:35:26 +01:00
spec . update (
dict (
2015-04-15 23:51:36 +02:00
region = dict ( aliases = [ ' aws_region ' , ' ec2_region ' ] ) ,
2014-02-09 00:35:26 +01:00
)
2014-02-05 12:11:06 +01:00
)
2014-02-09 00:35:26 +01:00
return spec
2014-02-05 12:11:06 +01:00
2014-02-05 12:11:06 +01:00
def boto_supports_profile_name ( ) :
return hasattr ( boto . ec2 . EC2Connection , ' profile_name ' )
2015-09-21 20:09:20 +02:00
def get_aws_connection_info ( module , boto3 = False ) :
2013-11-01 16:59:24 +01:00
# Check module args for credentials, then check environment vars
2014-02-05 12:11:06 +01:00
# access_key
2013-11-01 16:59:24 +01:00
ec2_url = module . params . get ( ' ec2_url ' )
2014-02-05 12:11:06 +01:00
access_key = module . params . get ( ' aws_access_key ' )
secret_key = module . params . get ( ' aws_secret_key ' )
security_token = module . params . get ( ' security_token ' )
2013-11-01 16:59:24 +01:00
region = module . params . get ( ' region ' )
2014-02-05 12:11:06 +01:00
profile_name = module . params . get ( ' profile ' )
validate_certs = module . params . get ( ' validate_certs ' )
2013-11-01 16:59:24 +01:00
if not ec2_url :
2014-12-25 21:31:34 +01:00
if ' AWS_URL ' in os . environ :
2013-11-01 16:59:24 +01:00
ec2_url = os . environ [ ' AWS_URL ' ]
2014-12-25 21:31:34 +01:00
elif ' EC2_URL ' in os . environ :
ec2_url = os . environ [ ' EC2_URL ' ]
2013-11-01 16:59:24 +01:00
2014-02-05 12:11:06 +01:00
if not access_key :
2014-12-25 21:31:34 +01:00
if ' AWS_ACCESS_KEY_ID ' in os . environ :
2014-02-05 12:11:06 +01:00
access_key = os . environ [ ' AWS_ACCESS_KEY_ID ' ]
2013-11-04 06:24:53 +01:00
elif ' AWS_ACCESS_KEY ' in os . environ :
2014-02-05 12:11:06 +01:00
access_key = os . environ [ ' AWS_ACCESS_KEY ' ]
2014-12-25 21:31:34 +01:00
elif ' EC2_ACCESS_KEY ' in os . environ :
access_key = os . environ [ ' EC2_ACCESS_KEY ' ]
2014-02-05 12:11:06 +01:00
else :
# in case access_key came in as empty string
access_key = None
2013-11-01 16:59:24 +01:00
2014-02-05 12:11:06 +01:00
if not secret_key :
2014-12-25 21:31:34 +01:00
if ' AWS_SECRET_ACCESS_KEY ' in os . environ :
2014-02-05 12:11:06 +01:00
secret_key = os . environ [ ' AWS_SECRET_ACCESS_KEY ' ]
2013-11-01 16:59:24 +01:00
elif ' AWS_SECRET_KEY ' in os . environ :
2014-02-05 12:11:06 +01:00
secret_key = os . environ [ ' AWS_SECRET_KEY ' ]
2014-12-25 21:31:34 +01:00
elif ' EC2_SECRET_KEY ' in os . environ :
secret_key = os . environ [ ' EC2_SECRET_KEY ' ]
2014-02-05 12:11:06 +01:00
else :
# in case secret_key came in as empty string
secret_key = None
2013-11-01 16:59:24 +01:00
if not region :
2014-12-25 21:31:34 +01:00
if ' AWS_REGION ' in os . environ :
2013-11-01 16:59:24 +01:00
region = os . environ [ ' AWS_REGION ' ]
2015-10-09 10:25:38 +02:00
elif ' AWS_DEFAULT_REGION ' in os . environ :
region = os . environ [ ' AWS_DEFAULT_REGION ' ]
2014-12-25 21:31:34 +01:00
elif ' EC2_REGION ' in os . environ :
region = os . environ [ ' EC2_REGION ' ]
2014-02-07 13:30:16 +01:00
else :
2015-11-14 02:19:09 +01:00
if not boto3 :
# boto.config.get returns None if config not found
region = boto . config . get ( ' Boto ' , ' aws_region ' )
if not region :
region = boto . config . get ( ' Boto ' , ' ec2_region ' )
elif boto3 and HAS_BOTO3 :
# here we don't need to make an additional call, will default to 'us-east-1' if the below evaluates to None.
region = botocore . session . get_session ( ) . get_config_variable ( ' region ' )
2013-11-01 16:59:24 +01:00
2014-02-05 12:11:06 +01:00
if not security_token :
if ' AWS_SECURITY_TOKEN ' in os . environ :
security_token = os . environ [ ' AWS_SECURITY_TOKEN ' ]
2014-12-25 21:31:34 +01:00
elif ' EC2_SECURITY_TOKEN ' in os . environ :
security_token = os . environ [ ' EC2_SECURITY_TOKEN ' ]
2014-02-05 12:11:06 +01:00
else :
# in case security_token came in as empty string
security_token = None
2015-09-21 20:09:20 +02:00
if HAS_BOTO3 and boto3 :
2015-07-14 23:30:51 +02:00
boto_params = dict ( aws_access_key_id = access_key ,
aws_secret_access_key = secret_key ,
aws_session_token = security_token )
if validate_certs :
boto_params [ ' verify ' ] = validate_certs
if profile_name :
boto_params [ ' profile_name ' ] = profile_name
else :
boto_params = dict ( aws_access_key_id = access_key ,
aws_secret_access_key = secret_key ,
security_token = security_token )
2014-02-05 12:11:06 +01:00
2015-12-01 04:03:07 +01:00
# profile_name only works as a key in boto >= 2.24
2015-07-14 23:30:51 +02:00
# so only set profile_name if passed as an argument
if profile_name :
if not boto_supports_profile_name ( ) :
module . fail_json ( " boto does not support profile_name before 2.24 " )
boto_params [ ' profile_name ' ] = profile_name
2014-02-05 12:11:06 +01:00
2015-07-14 23:30:51 +02:00
if validate_certs and HAS_LOOSE_VERSION and LooseVersion ( boto . Version ) > = LooseVersion ( " 2.6.0 " ) :
boto_params [ ' validate_certs ' ] = validate_certs
2014-02-05 12:11:06 +01:00
2015-12-01 16:03:57 +01:00
for param , value in boto_params . items ( ) :
if isinstance ( value , str ) :
boto_params [ param ] = unicode ( value , ' utf-8 ' , ' strict ' )
2014-02-05 12:11:06 +01:00
return region , ec2_url , boto_params
def get_ec2_creds ( module ) :
''' for compatibility mode with old modules that don ' t/can ' t yet
use ec2_connect method '''
region , ec2_url , boto_params = get_aws_connection_info ( module )
return ec2_url , boto_params [ ' aws_access_key_id ' ] , boto_params [ ' aws_secret_access_key ' ] , region
def boto_fix_security_token_in_profile ( conn , profile_name ) :
''' monkey patch for boto issue boto/boto#2100 '''
profile = ' profile ' + profile_name
if boto . config . has_option ( profile , ' aws_security_token ' ) :
conn . provider . set_security_token ( boto . config . get ( profile , ' aws_security_token ' ) )
return conn
def connect_to_aws ( aws_module , region , * * params ) :
conn = aws_module . connect_to_region ( region , * * params )
2014-11-01 03:36:31 +01:00
if not conn :
if region not in [ aws_module_region . name for aws_module_region in aws_module . regions ( ) ] :
2015-12-01 04:03:07 +01:00
raise AnsibleAWSError ( " Region %s does not seem to be available for aws module %s . If the region definitely exists, you may need to upgrade boto or extend with endpoints_path " % ( region , aws_module . __name__ ) )
2014-11-01 03:36:31 +01:00
else :
2015-12-01 04:03:07 +01:00
raise AnsibleAWSError ( " Unknown problem connecting to region %s for aws module %s . " % ( region , aws_module . __name__ ) )
2014-02-05 12:11:06 +01:00
if params . get ( ' profile_name ' ) :
conn = boto_fix_security_token_in_profile ( conn , params [ ' profile_name ' ] )
return conn
2013-12-17 03:04:12 +01:00
def ec2_connect ( module ) :
""" Return an ec2 connection """
2014-02-05 12:11:06 +01:00
region , ec2_url , boto_params = get_aws_connection_info ( module )
2013-12-17 03:04:12 +01:00
# If we have a region specified, connect to its endpoint.
if region :
try :
2014-02-05 12:11:06 +01:00
ec2 = connect_to_aws ( boto . ec2 , region , * * boto_params )
2015-12-01 04:03:07 +01:00
except ( boto . exception . NoAuthHandlerFound , AnsibleAWSError ) , e :
2014-02-05 12:11:06 +01:00
module . fail_json ( msg = str ( e ) )
2013-12-17 03:04:12 +01:00
# Otherwise, no region so we fallback to the old connection method
elif ec2_url :
try :
2014-02-05 12:11:06 +01:00
ec2 = boto . connect_ec2_endpoint ( ec2_url , * * boto_params )
2015-12-01 04:03:07 +01:00
except ( boto . exception . NoAuthHandlerFound , AnsibleAWSError ) , e :
2014-02-05 12:11:06 +01:00
module . fail_json ( msg = str ( e ) )
2013-12-17 03:04:12 +01:00
else :
module . fail_json ( msg = " Either region or ec2_url must be specified " )
2014-02-05 12:11:06 +01:00
return ec2