
    Vh`4                         d dl mZ d dlmZ 	 d dlZd dlmZ d dlm	Z	  G d d      Z
 G d d	      Z G d
 de      Zy# e$ r Y 3w xY w)    deepcopywrapsN)camel_dict_to_snake_dict)boto3_tag_list_to_ansible_dictc                   :    e Zd ZdZdZdZd Zed        Zd Z	d Z
y)BaseWaiterFactoryag  
    A helper class used for creating additional waiters.
    Unlike the waiters available directly from botocore these waiters will
    automatically retry on common (temporary) AWS failures.

    This class should be treated as an abstract class and subclassed before use.
    A subclass should:
    - create the necessary client to pass to BaseWaiterFactory.__init__
    - override _BaseWaiterFactory._waiter_model_data to return the data defining
      the waiter

    Usage:
    waiter_factory = BaseWaiterFactory(module, client)
    waiter = waiters.get_waiter('my_waiter_name')
    waiter.wait(**params)
    Nc                     || _         || _        | j                  | j                        }t        j
                  j                  t        d|            | _        y )N   )versionwaiters)waiter_config)	moduleclient_inject_ratelimit_retries_waiter_model_databotocorewaiterWaiterModeldict_model)selfr   r   datas       k/home/dcms/DCMS/lib/python3.12/site-packages/ansible_collections/community/aws/plugins/module_utils/base.py__init__zBaseWaiterFactory.__init__.   sM     --d.E.EFoo11q$7 2 
    c                     t               S )at  
        Subclasses should override this method to return a dictionary mapping
        waiter names to the waiter definition.

        This data is similar to the data found in botocore's waiters-2.json
        files (for example: botocore/botocore/data/ec2/2016-11-15/waiters-2.json)
        with two differences:
        1) Waiter names do not have transformations applied during lookup
        2) Only the 'waiters' data is required, the data is assumed to be
           version 2

        for example:

        @property
        def _waiter_model_data(self):
            return dict(
                tgw_attachment_deleted=dict(
                    operation='DescribeTransitGatewayAttachments',
                    delay=5, maxAttempts=120,
                    acceptors=[
                        dict(state='retry', matcher='pathAll', expected='deleting', argument='TransitGatewayAttachments[].State'),
                        dict(state='success', matcher='pathAll', expected='deleted', argument='TransitGatewayAttachments[].State'),
                        dict(state='success', matcher='path', expected=True, argument='length(TransitGatewayAttachments[]) == `0`'),
                        dict(state='success', matcher='error', expected='InvalidRouteTableID.NotFound'),
                    ]
                ),
            )

        or

        @property
        def _waiter_model_data(self):
            return {
                "instance_exists": {
                    "delay": 5,
                    "maxAttempts": 40,
                    "operation": "DescribeInstances",
                    "acceptors": [
                        {
                            "matcher": "path",
                            "expected": true,
                            "argument": "length(Reservations[]) > `0`",
                            "state": "success"
                        },
                        {
                            "matcher": "error",
                            "expected": "InvalidInstanceID.NotFound",
                            "state": "retry"
                        }
                    ]
                },
            }
        r   r   s    r   r   z$BaseWaiterFactory._waiter_model_data9   s    p vr   c                     g d}g }|D ]  }|j                  t        dd|             ! t        |      }|D ]  }||   d   j                  |        |S )N)RequestLimitExceededUnavailableServiceUnavailableInternalFailureInternalErrorTooManyRequestsException
Throttlingretryerror)statematcherexpected	acceptors)appendr   r   extend)r   modelextra_retriesr.   r*   r   r   s          r   r   z+BaseWaiterFactory._inject_ratelimit_retriess   sp    
 	" 	SET5QR	S % 	:F6N;'..y9	: r   c                     | j                   j                  }||vr!| j                  j                  d| d|        t        j
                  j                  || j                   | j                        S )NzUnable to find waiter z.  Available_waiters: )r   waiter_namesr   	fail_jsonr   r   create_waiter_with_clientr   )r   waiter_namer   s      r   
get_waiterzBaseWaiterFactory.get_waiter   sd    ++**g%KK!!$:;-G]^e]f"gh88KKKK
 	
r   )__name__
__module____qualname____doc__r   r   r   propertyr   r   r8    r   r   r
   r
      s7    " FF	
 7 7r*
r   r
   c                   *    e Zd Zed        ZddZd Zy)
Boto3Mixinc                       fd}|S )a  
        A simple wrapper that handles the usual botocore exceptions and exits
        with module.fail_json_aws.  Designed to be used with BaseResourceManager.
        Assumptions:
          1) First argument (usually `self` of method being wrapped will have a
             'module' attribute which is an AnsibleAWSModule
          2) First argument of method being wrapped will have an
            _extra_error_output() method which takes no arguments and returns a
            dictionary of extra parameters to be returned in the event of a
            botocore exception.
        Parameters:
          description (string): In the event of a botocore exception the error
                                message will be 'Failed to {DESCRIPTION}'.

        Example Usage:
            class ExampleClass(Boto3Mixin):
                def __init__(self, module)
                    self.module = module
                    self._get_client()

                @Boto3Mixin.aws_error_handler("connect to AWS")
                def _get_client(self):
                    self.client = self.module.client('ec2')

                @Boto3Mixin.aws_error_handler("describe EC2 instances")
                def _do_something(**params):
                    return self.client.describe_instances(**params)
        c                 2     t                fd       }|S )Nc                    | j                         }	  | g|i |S # t        j                  j                  $ r,} | j                  j
                  |fdd i| Y d }~y d }~wt        j                  j                  t        j                  j                  f$ r,} | j                  j
                  |fdd i| Y d }~y d }~ww xY w)NmsgzFailed waiting for z
Failed to )_extra_error_outputr   
exceptionsWaiterErrorr   fail_json_awsClientErrorBotoCoreError)_selfargskwargsextra_ouputedescriptionfuncs        r   handlerz>Boto3Mixin.aws_error_handler.<locals>.wrapper.<locals>.handler   s    #779a7777**66 j.ELL..qi8KK=6Yi]hi ++779L9L9Z9Z[ a.ELL..q`
;-6P`T_`as     C	"A##:C	"CC	r   )rQ   rR   rP   s   ` r   wrapperz-Boto3Mixin.aws_error_handler.<locals>.wrapper   s"    4[a a Nr   r>   )rP   rS   s   ` r   aws_error_handlerzBoto3Mixin.aws_error_handler   s    >	 r   c                 |    |y|j                  dd      }|rt        |      }n|s|i }t        |      }|||d<   |S )a+  
        Performs common boto3 resource to Ansible resource conversion.
        `resource['Tags']` will by default be converted from the boto3 tag list
        format to a simple dictionary.
        Parameters:
          resource (dict): The boto3 style resource to convert to the normal Ansible
                           format (snake_case).
          add_tags (bool): When `true`, if a resource does not have 'Tags' property
                           the returned resource will have tags set to an empty
                           dictionary.
        NTagstags)getr   r   )r   resourceadd_tagsrW   normalized_resources        r   _normalize_boto3_resourcez$Boto3Mixin._normalize_boto3_resource   sY     ||FD)1$7D)D6x@*.'""r   c                     t               S Nr   r    s    r   rE   zBoto3Mixin._extra_error_output   s     vr   N)F)r9   r:   r;   staticmethodrT   r\   rE   r>   r   r   r@   r@      s    + +Z#4r   r@   c                        e Zd Z fdZddZd Zd Zd Zd Ze	d        Z
d Zd	 Zd
 Zd Zd Zd Zd Zd ZddZddZd Zd Z xZS )BaseResourceManagerc                     || _         d| _        t               | _        t               | _        t               | _        t               | _        d| _        d| _        t        t        | /          y)zW
        Parameters:
            module (AnsibleAWSModule): An Ansible module.
        FTN)r   changedr   original_resourceupdated_resource_resource_updates_preupdate_resource_wait_wait_timeoutsuperra   r   )r   r   	__class__s     r   r   zBaseResourceManager.__init__   sX    
 !% $!%#'6 
!!413r   c                     t        | j                        }|j                  | j                         |r| j	                  |      }|S )zz
        Merges the contents of the 'pre_update' resource and metadata variables
        with the pending updates
        )r   rg   updaterf   %_filter_immutable_resource_attributes)r   filter_immutablecreationrY   s       r   _merge_resource_changesz+BaseResourceManager._merge_resource_changes   s>    
 D445../AA(KHr   c                     t        |      S r^   r   )r   rY   s     r   rn   z9BaseResourceManager._filter_immutable_resource_attributes   s    !!r   c                      y r^   r>   r   paramss     r   _do_creation_waitz%BaseResourceManager._do_creation_wait      r   c                      y r^   r>   rt   s     r   _do_deletion_waitz%BaseResourceManager._do_deletion_wait  rw   r   c                      y r^   r>   rt   s     r   _do_update_waitz#BaseResourceManager._do_update_wait  rw   r   c                     t               }| j                  r7t        d| j                        }| j                  |z  }t        ||      }||d<   |S )N   )DelayMaxAttemptsWaiterConfig)r   ri   min)r   ru   delaymax_attemptsconfigs        r   _waiter_configz"BaseResourceManager._waiter_config  sO    4--.E--6L<@F%+F>"r   c                 Z    | j                   sy | j                  } | j                  di | y Nr>   )rh   r   ry   rt   s     r   _wait_for_deletionz&BaseResourceManager._wait_for_deletion  *    zz$$((r   c                 Z    | j                   sy | j                  } | j                  di | y r   )rh   r   rv   rt   s     r   _wait_for_creationz&BaseResourceManager._wait_for_creation  r   r   c                 Z    | j                   sy | j                  } | j                  di | y r   )rh   r   r{   rt   s     r   _wait_for_updatez$BaseResourceManager._wait_for_update!  s*    zz$$&v&r   c                 &    | j                  d      S )z
        Merges all pending changes into self.updated_resource
        Used during check mode where it's not possible to get and
        refresh the resource
        F)ro   )rq   r    s    r   _generate_updated_resourcez.BaseResourceManager._generate_updated_resource'  s     ++U+CCr   c                 4   d}| j                   j                  sF| j                         }| j                          | j	                          | j                         | _        n$| j                  | j                               | _        t               | _
        || _        y)NT)r   
check_mode_do_create_resourcer   rv   get_resourcere   _normalize_resourcer   r   rf   rc   )r   rc   s     r   _flush_createz!BaseResourceManager._flush_create1  s{    {{%%..0G##%""$$($5$5$7D!$($<$<T=\=\=^$_D!!%r   c                     | j                   ryy)NTF)rf   r    s    r   _check_updates_pendingz*BaseResourceManager._check_updates_pending@  s    !!r   c                 F   | j                         s| j                  | _        y| j                  j                  s6| j                          | j                          | j                         | _        n$| j                  | j                               | _        t               | _        yNFT)r   rd   re   r   r   _do_update_resourcer   r   r   r   r   rf   r    s    r   _flush_updatez!BaseResourceManager._flush_updateH  s~    **,$($:$:D!{{%%$$&!!#$($5$5$7D!$($<$<T=\=\=^$_D!!%r   c                 Z    | j                   r| j                         S | j                         S r^   )rd   r   r   r    s    r   flush_changesz!BaseResourceManager.flush_changesW  s)    !!%%''%%''r   c                     |y|| j                  |      k(  ry|r/| j                  r#||}| j                  j                  | d       || j                  |<   d| _        y)NFz" can not be updated after creation)rD   T)_get_resource_valuerd   r   r5   rf   rc   )r   keyvaluerP   	immutables        r   _set_resource_valuez'BaseResourceManager._set_resource_value]  sm    =D,,S11//"!KK!!5W&X!Y&+s#r   c                 r    | j                   j                  ||      }| j                  j                  ||      S r^   )rg   rX   rf   )r   r   defaultdefault_values       r   r   z'BaseResourceManager._get_resource_valuej  s3    0044S'B%%))#}==r   c                 8    |y|| j                   k(  ry|| _         yr   )rh   )r   waits     r   set_waitzBaseResourceManager.set_waitn  s#    <4::
r   c                 8    |y|| j                   k(  ry|| _         yr   )ri   )r   timeouts     r   set_wait_timeoutz$BaseResourceManager.set_wait_timeoutw  s&    ?d((($r   )TF)NFr^   )r9   r:   r;   r   rq   rn   rv   ry   r{   r=   r   r   r   r   r   r   r   r   r   r   r   r   r   __classcell__)rk   s   @r   ra   ra      sr    4"  ))'D(>r   ra   )copyr   	functoolsr   r   ImportError0ansible.module_utils.common.dict_transformationsr   ;ansible_collections.amazon.aws.plugins.module_utils.taggingr   r
   r@   ra   r>   r   r   <module>r      s[     	 V fw
 w
tL L^\* \c  		s   > AA