B
    `|                 @   sz  d dl mZmZmZ eZd dlZd dlZd dlZd dl	Z	d dl
mZ d dl
mZ d dlmZ d dlmZ d dlmZ d dlmZ d d	lmZ d d
lmZ d dlmZ d dlmZ d dlmZ d dlmZ d dlmZ ddlmZ dZyd dlZd dl ZdZ!W n" e"k
r   e	# ZdZ!Y nX dZ$yd dl%Z%d dl&Z&dZ'W n" e"k
r^   e	# Z$dZ'Y nX yd dl(m)Z) dZ*W n e"k
r   dZ*Y nX G dd de+Z,dd Z-G dd deZ.dHddZ/dIddZ0e0Z1dd  Z2d!d" Z3d#d$ Z4dJd%d&Z5dKd'd(Z6d)d* Z7d+d, Z8d-d. Z9d/d0 Z:d1d2 Z;dLd3d4Z<dMd7d8Z=dNd9d:Z>d;d< Z?d=d> Z@dOd@dAZAdBdC ZBdDdE ZCdPdFdGZDdS )Q    )absolute_importdivisionprint_functionN)	to_native)to_text)__version__)env_fallback)missing_required_lib)binary_type)string_types)	text_type)integer_types)_camel_to_snake)_snake_to_camel)camel_dict_to_snake_dict)snake_dict_to_camel_dict   )
CloudRetryTF)
cmp_to_keyc               @   s   e Zd ZdS )AnsibleAWSErrorN)__name__
__module____qualname__ r   r   f/home/dcms/DCMS/lib/python3.7/site-packages/ansible_collections/amazon/aws/plugins/module_utils/ec2.pyr   Q   s   r   c               C   s   t rtjjS tdS )z
    Allow for boto3 not being installed when using these utils by wrapping
    botocore.exceptions instead of assigning from it directly.
    N)	HAS_BOTO3botocore
exceptionsZClientErrortyper   r   r   r   _botocore_exception_maybeU   s    r   c               @   s,   e Zd Ze Zedd ZedddZdS )AWSRetryc             C   s   | j d d S )NErrorZCode)response)errorr   r   r   status_code_from_exceptionb   s    z#AWSRetry.status_code_from_exceptionNc             C   s(   dddddddg}|r | | | |kS )NZRequestLimitExceededZUnavailableZServiceUnavailableZInternalFailureZInternalErrorZTooManyRequestsExceptionZ
Throttling)extend)Zresponse_codeZcatch_extra_error_codesZretry_onr   r   r   foundf   s    
zAWSRetry.found)N)r   r   r   r   Z
base_classstaticmethodr$   r&   r   r   r   r   r    _   s   r    c             K   s   yt f ||||d|S  tk
rN } z| jdt| d W d d }~X Y n tjjtjjtjjtjj	fk
r } z| jt|d W d d }~X Y n8 tjj
k
r } z| jd| j d W d d }~X Y nX d S )N)	conn_typeresourceregionendpointzCouldn't connect to AWS: %s)msgzoThe %s module requires a region and none was found in configuration, environment variables or module parameters)_boto3_conn
ValueError	fail_jsonr   r   r   ProfileNotFoundZPartialCredentialsErrorZNoCredentialsErrorZConfigParseErrorZNoRegionError_name)moduler(   r)   r*   r+   paramser   r   r   
boto3_conn}   s    $ r5   c       	      K   s   | dd }| dkrtdtjjdtd}|dd k	rN|| d}|dd k	rl|| d}t	j
j|d}| d	kr|j|f|||d
|S | dkr|j|f|||d
|S |j|f||d|}|j|f||d|}||fS d S )Nprofile_name)Zbothr)   clientzThere is an issue in the calling code. You must specify either both, resource, or client to the conn_type parameter in the boto3_conn function callzAnsible/{0})Zuser_agent_extraconfig
aws_config)r6   r)   )r8   region_nameendpoint_urlr7   )r:   r;   )popr.   r   r8   Configformatr   getmergeboto3sessionSessionr)   r7   )	r(   r)   r*   r+   r3   profiler8   rB   r7   r   r   r   r-      s$    r-   c             C   sT   t | dr| j}n>t | drDt| jd t|  d tt|  }ndt| f }|S )z{
    Extracts the error message from a boto exception.

    :param err: Exception from boto
    :return: Error message
    error_messagemessage z - z%s: %s)hasattrrE   strrF   r   	Exception)errr#   r   r   r   boto_exception   s    

(rL   c               C   sr   t t tdgfdddt ddgdt dd	gdt d
dgddt ddgddt dddt ddt dgdt ddd	S )NZANSIBLE_DEBUG_BOTOCORE_LOGSFbool)fallbackdefaultr   Zaws_endpoint_urlr;   )aliasesZec2_access_key
access_keyZec2_secret_key
secret_keyT)rP   Zno_logZaccess_tokenaws_security_token)rO   r   path)r   Zaws_profiledict)	Zdebug_botocore_endpoint_logsec2_urlaws_access_keyaws_secret_keysecurity_tokenvalidate_certsaws_ca_bundlerD   r9   )rU   r   r   r   r   r   aws_common_argument_spec   s    

r\   c              C   s$   t  } | ttddgdd | S )N
aws_region
ec2_region)rP   )r*   )r\   updaterU   )specr   r   r   ec2_argument_spec   s
    ra   c          
   C   s   | j d}|r|S dtjkr(tjd S dtjkr<tjd S dtjkrPtjd S |stsj| jtdtd tj	dd}|r|S tj	dd	S t
s| jtd
td y | j d}tjj|ddS  tjjk
r } zd S d }~X Y nX d S )Nr*   Z
AWS_REGIONZAWS_DEFAULT_REGIONZ
EC2_REGIONboto)r,   	exceptionZBotor]   r^   rA   rD   )rD   )r3   r?   osenvironHAS_BOTOr/   r	   BOTO_IMP_ERRrb   r8   r   BOTO3_IMP_ERRr   rB   rC   Zget_config_variabler   r0   )r2   rA   r*   r6   r4   r   r   r   get_aws_region   s.    





ri   c             C   s  | j d}| j d}| j d}| j d}t| |}| j d}| j d}| j d}	| j d}
|s|s|stjd	rtjd	}tjd
rtjd
}|r|s|s|r| jdddd |sdtjkrtjd }ndtjkrtjd }|stjdrtjd }ntjdr.tjd }njtjdrHtjd }nPtrntjddrntjdd}n*trtjddrtjdd}nd }|s<tjdrtjd }ntjdrtjd }njtjdrtjd }nPtrtjddrtjdd}n*tr8tjddr8tjdd}nd }|stjdr\tjd }ntjdrvtjd }njtjdrtjd }nPtrtjddrtjdd}n*trtjddrtjdd}nd }|	s tjdr tjd}	t	rV|rVt
|||d }|r6t
d d d d }||d!< |rL|	rL|	|d"< n||d"< n$t
|||d#}|rr||d!< ||d< |
d k	rt	r|rtjjf |
|d< n&tr|sd$|
kr|
d$ tjd% _x2| D ]&\}}t|trt|d&d'||< qW |||fS )(NrV   rW   rX   rY   rD   rZ   r[   r9   ZAWS_PROFILEZAWS_DEFAULT_PROFILEzPassing both a profile and access tokens has been deprecated.  Only the profile will be used.  In later versions of Ansible the options will be mutually exclusivez
2022-06-01z
amazon.aws)dateZcollection_nameZAWS_URLZEC2_URLZAWS_ACCESS_KEY_IDZAWS_ACCESS_KEYZEC2_ACCESS_KEYZCredentialsaws_access_key_idrO   ZAWS_SECRET_ACCESS_KEYZAWS_SECRET_KEYZEC2_SECRET_KEYaws_secret_access_keyZAWS_SECURITY_TOKENZAWS_SESSION_TOKENZEC2_SECURITY_TOKENrS   ZAWS_CA_BUNDLE)rk   rl   Zaws_session_tokenr6   verify)rk   rl   rY   
user_agentzboto.connectionzutf-8strict)r3   r?   ri   rd   re   Z	deprecaterf   rb   r8   r   rU   r   r=   sysmodulesZ	UserAgentitems
isinstancer
   r   )r2   rA   rV   rQ   rR   rY   r*   r6   rZ   Z	ca_bundler8   boto_paramsparamvaluer   r   r   get_aws_connection_info   s    








rw   c             C   s"   t | \}}}||d |d |fS )z] for compatibility mode with old modules that don't/can't yet
        use ec2_connect method rk   rl   )rw   )r2   r*   rV   rt   r   r   r   get_ec2_credsr  s    rx   c             C   s0   d| }t j|dr,| jt j|d | S )z, monkey patch for boto issue boto/boto#2100 zprofile rS   )rb   r8   
has_optionproviderZset_security_tokenr?   )connr6   rD   r   r   r   "boto_fix_security_token_in_profiley  s    r|   c             K   s   y| j |f|}W n  tjjk
r2   tdY nX |st|dd |  D krbtd|| jf ntd|| jf |drt||d }|S )Nz;Profile given for AWS was not found.  Please fix and retry.c             S   s   g | ]
}|j qS r   )name).0Zaws_module_regionr   r   r   
<listcomp>  s    z"connect_to_aws.<locals>.<listcomp>zRegion %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_pathz:Unknown problem connecting to region %s for aws module %s.r6   )	Zconnect_to_regionrb   rz   ProfileNotFoundErrorr   Zregionsr   r?   r|   )Z
aws_moduler*   r3   r{   r   r   r   connect_to_aws  s    
r   c          
   C   s   t | \}}}|rhytj|f|}W q tjjttjjfk
rd } z| jt	|d W dd}~X Y qX nh|ryt
tj|f|}W q tjjttjjfk
r } z| jt	|d W dd}~X Y qX n| jdd |S )z Return an ec2 connection)r,   Nz*Either region or ec2_url must be specified)rw   rb   Zconnect_ec2_endpointrc   ZNoAuthHandlerFoundr   rz   r   r/   rI   r   ec2)r2   r*   rV   rt   r   r4   r   r   r   ec2_connect  s    $$r   c             C   s   g }x||   D ]p\}}d|i}t|tr<t| g|d< n8t|trVt|g|d< nt|trl|g|d< n||d< || qW |S )a'   Convert an Ansible dict of filters to list of dicts that boto3 can use
    Args:
        filters_dict (dict): Dict of AWS filters.
    Basic Usage:
        >>> filters = {'some-aws-id': 'i-01234567'}
        >>> ansible_dict_to_boto3_filter_list(filters)
        {
            'some-aws-id': 'i-01234567'
        }
    Returns:
        List: List of AWS filters and their values
        [
            {
                'Name': 'some-aws-id',
                'Values': [
                    'i-01234567',
                ]
            }
        ]
    NameValues)rr   rs   rM   rI   lowerr   r   append)Zfilters_dictZfilters_listkvZfilter_dictr   r   r   !ansible_dict_to_boto3_filter_list  s    


r   c                s   |r|r||i}n
ddd}| r2t dd | D s6i S xD| D ]8\  | d kr@| d kr@t fdd| D S q@W tdt|t| f d	S )
a   Convert a boto3 list of resource tags to a flat dict of key:value pairs
    Args:
        tags_list (list): List of dicts representing AWS tags.
        tag_name_key_name (str): Value to use as the key for all tag keys (useful because boto3 doesn't always use "Key")
        tag_value_key_name (str): Value to use as the key for all tag values (useful because boto3 doesn't always use "Value")
    Basic Usage:
        >>> tags_list = [{'Key': 'MyTagKey', 'Value': 'MyTagValue'}]
        >>> boto3_tag_list_to_ansible_dict(tags_list)
        [
            {
                'Key': 'MyTagKey',
                'Value': 'MyTagValue'
            }
        ]
    Returns:
        Dict: Dict of key:value pairs representing AWS tags
         {
            'MyTagKey': 'MyTagValue',
        }
    rv   Value)keyKeyc             s   s   | ]
}|V  qd S )Nr   )r~   tagr   r   r   	<genexpr>  s    z1boto3_tag_list_to_ansible_dict.<locals>.<genexpr>r   c             3   s   | ]}|  | fV  qd S )Nr   )r~   r   )r   r   r   r   r     s    z4Couldn't find tag key (candidates %s) in tag list %sN)anyrr   rU   r.   rI   )	tags_listtag_name_key_nametag_value_key_nameZtag_candidatesr   )r   r   r   boto3_tag_list_to_ansible_dict  s    

r   r   r   c             C   s4   g }x*|   D ]\}}||||t|i qW |S )a   Convert a flat dict of key:value pairs representing AWS resource tags to a boto3 list of dicts
    Args:
        tags_dict (dict): Dict representing AWS resource tags.
        tag_name_key_name (str): Value to use as the key for all tag keys (useful because boto3 doesn't always use "Key")
        tag_value_key_name (str): Value to use as the key for all tag values (useful because boto3 doesn't always use "Value")
    Basic Usage:
        >>> tags_dict = {'MyTagKey': 'MyTagValue'}
        >>> ansible_dict_to_boto3_tag_list(tags_dict)
        {
            'MyTagKey': 'MyTagValue'
        }
    Returns:
        List: List of dicts containing tag keys and values
        [
            {
                'Key': 'MyTagKey',
                'Value': 'MyTagValue'
            }
        ]
    )rr   r   r   )Z	tags_dictr   r   r   r   r   r   r   r   ansible_dict_to_boto3_tag_list  s    r   c       	         s  dd dd g }t | tr$| g}  rZ|rLd|gdg}|j|dd }q|| d }n"|rtd|i}|j|d	}n| }t|  fd
d|D }tt| t| t|dkrdd |D |dd< dd |D }t|dkrtdd	| | fdd|D 7 }|S )ay   Return list of security group IDs from security group names. Note that security group names are not unique
     across VPCs.  If a name exists across multiple VPCs and no VPC ID is supplied, all matching IDs will be returned. This
     will probably lead to a boto exception if you attempt to assign both IDs to a resource so ensure you wrap the call in
     a try block
     c             S   s   |r| d S | j S d S )NZ	GroupName)r}   )sgrA   r   r   r   get_sg_name  s    z:get_ec2_security_group_ids_from_names.<locals>.get_sg_namec             S   s   |r| d S | j S d S )NZGroupId)id)r   rA   r   r   r   	get_sg_id#  s    z8get_ec2_security_group_ids_from_names.<locals>.get_sg_idzvpc-id)r   r   )ZFiltersZSecurityGroups)filtersc             3   s   | ]}t | V  qd S )N)rI   )r~   all_sg)rA   r   r   r   r   D  s    z8get_ec2_security_group_ids_from_names.<locals>.<genexpr>r   c             S   s   g | ]}t d |r|qS )zsg-[a-fA-F0-9]+$)rematch)r~   r   r   r   r   r   I  s    z9get_ec2_security_group_ids_from_names.<locals>.<listcomp>Nc             S   s   g | ]}t d |s|qS )zsg-[a-fA-F0-9]+$)r   r   )r~   r   r   r   r   r   J  s    z+The following group names are not valid: %sz, c                s,   g | ]$}t | krt | qS r   )rI   )r~   r   )rA   r   r   sec_group_name_listr   r   r   N  s    )
rs   r   Zdescribe_security_groupsZget_all_security_groupsset
differencelistlenr.   join)	Zsec_group_listZec2_connectionZvpc_idrA   Zsec_group_id_listr   Zall_sec_groups	unmatchedZstill_unmatchedr   )rA   r   r   r   r   %get_ec2_security_group_ids_from_names  s0    
r   c             C   s  t | trtt|  gS t | tr4tt| gS t | trtx| D ]*}t|g }t |trdt|}|| qDW nt | t	st | t
rt| } | dr| dr| dd } | gS t | tr:t|  }|  x`|D ]X}| | }|dkr
| | dkr
ddi}t|g }t |tr(t|}|||f qW t|dkr`t |d	 tr`|d	 }t |trtr|jttd
 n|  |S )a  
        Takes a policy and returns a list, the contents of which are all hashable and sorted.
        Example input policy:
        {'Version': '2012-10-17',
         'Statement': [{'Action': 's3:PutObjectAcl',
                        'Sid': 'AddCannedAcl2',
                        'Resource': 'arn:aws:s3:::test_policy/*',
                        'Effect': 'Allow',
                        'Principal': {'AWS': ['arn:aws:iam::XXXXXXXXXXXX:user/username1', 'arn:aws:iam::XXXXXXXXXXXX:user/username2']}
                       }]}
        Returned value:
        [('Statement',  ((('Action', (u's3:PutObjectAcl',)),
                          ('Effect', (u'Allow',)),
                          ('Principal', ('AWS', ((u'arn:aws:iam::XXXXXXXXXXXX:user/username1',), (u'arn:aws:iam::XXXXXXXXXXXX:user/username2',)))),
                          ('Resource', (u'arn:aws:s3:::test_policy/*',)), ('Sid', (u'AddCannedAcl2',)))),
         ('Version', (u'2012-10-17',)))]

    zarn:aws:iam::z:root:   )ZNotPrincipalZ	Principal*ZAWSr   r   )r   )rs   rM   tuplerI   r   intr   _hashable_policyr   r   r
   r   
startswithendswithsplitrU   keyssortr   PY3_COMPARISONr   py3cmp)policyZpolicy_listZeachZ
tupleifiedZsorted_keysr   elementr   r   r   r   S  sB    







r   c          
   C   s   y | |krdS | |k rdS dS W nd t k
r } zFt|d}t|d}d||fkrr||k rfdS ||k rrdS  W dd}~X Y nX dS )zh Python 2 can sort lists of mixed types. Strings < tuples. Without this function this fails on Python 3.r   r   rI   r   N)	TypeErrorr   find)abr4   Zstr_indZtup_indr   r   r   r     s    r   
2008-10-17c             C   s\   |r@t | tr"|  } | d| t |tr@| }|d| tt|g tt| g kS )zy Compares the existing policy and the updated policy
        Returns True if there is a difference between policies.
    Version)rs   rU   copy
setdefaultr   r   )Zcurrent_policyZ
new_policyZdefault_versionr   r   r   compare_policies  s    

r   c                sb    fdd i }xL|   D ]@\}}t|tr:t|||< qt|trR |||< q|||< qW |S )a1   Sort any lists in an IAM JSON policy so that comparison of two policies with identical values but
    different orders will return true
    Args:
        policy_dict (dict): Dict representing IAM JSON policy.
    Basic Usage:
        >>> my_iam_policy = {'Principle': {'AWS':["31","7","14","101"]}
        >>> sort_json_policy_dict(my_iam_policy)
    Returns:
        Dict: Will return a copy of the policy as a Dict but any List will be sorted
        {
            'Principle': {
                'AWS': [ '7', '14', '31', '101' ]
            }
        }
    c                sd   g }xJ| D ]B}t |tr(|t| q
t |trB| | q
|| q
W |jdd d |S )Nc             S   s   t | trt|  S | S )N)rs   rU   sortedrr   )xr   r   r   <lambda>      z>sort_json_policy_dict.<locals>.value_is_list.<locals>.<lambda>)r   )rs   rU   r   sort_json_policy_dictr   r   )Zmy_listZchecked_listitem)value_is_listr   r   r     s    


z,sort_json_policy_dict.<locals>.value_is_list)rr   rs   rU   r   r   )Zpolicy_dictZordered_policy_dictr   rv   r   )r   r   r     s    

r   c             C   s   | dkrdS t |  }t| tr~x| D ]R}||krtt|| tr\t| | || d ||< qxt| | || ||< q&| S q&W nLt| trx@tt| D ]}|t| | | qW n|rt d | | S |S )a  
        Allows to cast elements within a dictionary to a specific type
        Example of usage:

        DEPLOYMENT_CONFIGURATION_TYPE_MAP = {
            'maximum_percent': 'int',
            'minimum_healthy_percent': 'int'
        }

        deployment_configuration = map_complex_type(module.params['deployment_configuration'],
                                                    DEPLOYMENT_CONFIGURATION_TYPE_MAP)

        This ensures all keys within the root element are casted and valid integers
    Nr   __builtins__)	r   rs   rU   r   map_complex_typeranger   r   globals)Zcomplex_typeZtype_mapnew_typer   ir   r   r   r     s,    




r   c             C   sx   i }g }x&|   D ]}||kr|r|| qW x>t|  t| D ]&}t|| | |krF|| ||< qFW ||fS )a  
    Compare two dicts of AWS tags. Dicts are expected to of been created using 'boto3_tag_list_to_ansible_dict' helper function.
    Two dicts are returned - the first is tags to be set, the second is any tags to remove. Since the AWS APIs differ
    these may not be able to be used out of the box.

    :param current_tags_dict:
    :param new_tags_dict:
    :param purge_tags:
    :return: tag_key_value_pairs_to_set: a dict of key value pairs that need to be set in AWS. If all tags are identical this dict will be empty
    :return: tag_keys_to_unset: a list of key names (type str) that need to be unset in AWS. If no tags need to be unset this list will be empty
    )r   r   r   r   r?   )Zcurrent_tags_dictZnew_tags_dictZ
purge_tagsZtag_key_value_pairs_to_setZtag_keys_to_unsetr   r   r   r   compare_aws_tags  s    r   )NNNN)NNNN)F)F)NN)r   r   )NT)r   )T)E
__future__r   r   r   r   __metaclass__rd   r   rp   	tracebackZansible.module_utils._textr   r   Z$ansible.module_utils.ansible_releaser   Zansible.module_utils.basicr   r	   Zansible.module_utils.sixr
   r   r   r   Z0ansible.module_utils.common.dict_transformationsr   r   r   r   Zcloudr   rg   rb   Zboto.ec2rf   ImportError
format_excrh   rA   r   r   	functoolsr   r   rJ   r   r   r    r5   r-   Zboto3_inventory_connrL   r\   ra   ri   rw   rx   r|   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   <module>   s   





 

!
{(
%

?>
.*