
    VhJ                     
   d Z dZdZddlZddlZddlZddlZddlZ	 ddl	m
Z
 d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ZddZddZd Zd Zd Zd Zd Zd Ze dk(  r e        yy# e$ r dZY Rw xY w# e$ r Y Xw xY w)a  
---
module: s3_sync
version_added: 1.0.0
short_description: Efficiently upload multiple files to S3
description:
- The S3 module is great, but it is very slow for a large volume of files- even a dozen will be noticeable. In addition to speed, it handles globbing,
  inclusions/exclusions, mime types, expiration mapping, recursion, cache control and smart directory mapping.
options:
  mode:
    description:
    - sync direction.
    default: 'push'
    choices: [ 'push' ]
    type: str
  file_change_strategy:
    description:
    - Difference determination method to allow changes-only syncing. Unlike rsync, files are not patched- they are fully skipped or fully uploaded.
    - date_size will upload if file sizes don't match or if local file modified date is newer than s3's version
    - checksum will compare etag values based on s3's implementation of chunked md5s.
    - force will always upload all files.
    required: false
    default: 'date_size'
    choices: [ 'force', 'checksum', 'date_size' ]
    type: str
  bucket:
    description:
    - Bucket name.
    required: true
    type: str
  key_prefix:
    description:
    - In addition to file path, prepend s3 path with this prefix. Module will add slash at end of prefix if necessary.
    required: false
    type: str
    default: ''
  file_root:
    description:
    - File/directory path for synchronization. This is a local path.
    - This root path is scrubbed from the key name, so subdirectories will remain as keys.
    required: true
    type: path
  permission:
    description:
    - Canned ACL to apply to synced files.
    - Changing this ACL only changes newly synced files, it does not trigger a full reupload.
    required: false
    choices:
    - 'private'
    - 'public-read'
    - 'public-read-write'
    - 'authenticated-read'
    - 'aws-exec-read'
    - 'bucket-owner-read'
    - 'bucket-owner-full-control'
    type: str
  mime_map:
    description:
    - >
      Dict entry from extension to MIME type. This will override any default/sniffed MIME type.
      For example C({".txt": "application/text", ".yml": "application/text"})
    required: false
    type: dict
  include:
    description:
    - Shell pattern-style file matching.
    - Used before exclude to determine eligible files (for instance, only C("*.gif"))
    - For multiple patterns, comma-separate them.
    required: false
    default: "*"
    type: str
  exclude:
    description:
    - Shell pattern-style file matching.
    - Used after include to remove files (for instance, skip C("*.txt"))
    - For multiple patterns, comma-separate them.
    required: false
    default: ".*"
    type: str
  cache_control:
    description:
    - Cache-Control header set on uploaded objects.
    - Directives are separated by commas.
    required: false
    type: str
    default: ''
  storage_class:
    description:
    - Storage class to be associated to each object added to the S3 bucket.
    required: false
    choices:
    - 'STANDARD'
    - 'REDUCED_REDUNDANCY'
    - 'STANDARD_IA'
    - 'ONEZONE_IA'
    - 'INTELLIGENT_TIERING'
    - 'GLACIER'
    - 'DEEP_ARCHIVE'
    - 'OUTPOSTS'
    default: 'STANDARD'
    type: str
    version_added: 1.5.0
  delete:
    description:
    - Remove remote files that exist in bucket but are not present in the file root.
    required: false
    default: false
    type: bool

author:
- Ted Timmons (@tedder)
extends_documentation_fragment:
- amazon.aws.common.modules
- amazon.aws.region.modules
- amazon.aws.boto3
a  
- name: basic upload
  community.aws.s3_sync:
    bucket: tedder
    file_root: roles/s3/files/

- name: basic upload using the glacier storage class
  community.aws.s3_sync:
    bucket: tedder
    file_root: roles/s3/files/
    storage_class: GLACIER

- name: basic individual file upload
  community.aws.s3_sync:
    bucket: tedder
    file_root: roles/s3/files/file_name

- name: all the options
  community.aws.s3_sync:
    bucket: tedder
    file_root: roles/s3/files
    mime_map:
      .yml: application/text
      .json: application/text
    key_prefix: config_files/web
    file_change_strategy: force
    permission: public-read
    cache_control: "public, max-age=31536000"
    storage_class: "GLACIER"
    include: "*"
    exclude: "*.txt,.*"
a	  
filelist_initial:
  description: file listing (dicts) from initial globbing
  returned: always
  type: list
  sample: [{
                "bytes": 151,
                "chopped_path": "policy.json",
                "fullpath": "roles/cf/files/policy.json",
                "modified_epoch": 1477416706
           }]
filelist_local_etag:
  description: file listing (dicts) including calculated local etag
  returned: always
  type: list
  sample: [{
                "bytes": 151,
                "chopped_path": "policy.json",
                "fullpath": "roles/cf/files/policy.json",
                "mime_type": "application/json",
                "modified_epoch": 1477416706,
                "s3_path": "s3sync/policy.json"
           }]
filelist_s3:
  description: file listing (dicts) including information about previously-uploaded versions
  returned: always
  type: list
  sample: [{
                "bytes": 151,
                "chopped_path": "policy.json",
                "fullpath": "roles/cf/files/policy.json",
                "mime_type": "application/json",
                "modified_epoch": 1477416706,
                "s3_path": "s3sync/policy.json"
           }]
filelist_typed:
  description: file listing (dicts) with calculated or overridden mime types
  returned: always
  type: list
  sample: [{
                "bytes": 151,
                "chopped_path": "policy.json",
                "fullpath": "roles/cf/files/policy.json",
                "mime_type": "application/json",
                "modified_epoch": 1477416706
           }]
filelist_actionable:
  description: file listing (dicts) of files that will be uploaded after the strategy decision
  returned: always
  type: list
  sample: [{
                "bytes": 151,
                "chopped_path": "policy.json",
                "fullpath": "roles/cf/files/policy.json",
                "mime_type": "application/json",
                "modified_epoch": 1477931256,
                "s3_path": "s3sync/policy.json",
                "whysize": "151 / 151",
                "whytime": "1477931256 / 1477929260"
           }]
uploads:
  description: file listing (dicts) of files that were actually uploaded
  returned: always
  type: list
  sample: [{
                "bytes": 151,
                "chopped_path": "policy.json",
                "fullpath": "roles/cf/files/policy.json",
                "s3_path": "s3sync/policy.json",
                "whysize": "151 / 151",
                "whytime": "1477931637 / 1477931489"
           }]

    N)tzTFto_text)is_boto3_error_code)calculate_multipart_etag)AnsibleCommunityAWSModulec           	      l   g }t         j                  j                  |       rk| }t        j                  |      }| j	                  d      }|d   }|t
        j                     }|t
        j                     }	|j                  |||	|d       |S t        j                  |       D ]  \  }
}}|D ]  }t         j                  j                  |
|      }|r4d}|j	                  d      D ]  }t        j                  ||      sd} |sZ|r4d}|j	                  d      D ]  }t        j                  ||      sd} |rt         j                  j                  ||       }t        j                  |      }|t
        j                     }|t
        j                     }	|j                  |||	|d         |S )N/)fullpathchopped_pathmodified_epochbytesF,T)start)ospathisfilestatsplitosstatST_SIZEST_MTIMEappendwalkjoinfnmatchrelpath)filerootincludeexcluderetr   fstat
path_arrayr   f_sizef_modified_epochdirpathdirnames	filenamesfnfoundxs                   i/home/dcms/DCMS/lib/python3.12/site-packages/ansible_collections/community/aws/plugins/modules/s3_sync.pygather_filesr.     s   
C	ww~~h!^^C(
!"~v~~& 1

$ ,"2		
^ JK -/GGH,= !	(GXy  77<<4!E$]]3/ )"??2q1$(E) ! !E$]]3/ )"??2q1$(E)  !wwxxH)v~~.#(#9 

$,(4*:!'	3 !	J J    c                     g }| D ]I  }|j                         }t        j                  j                  ||d         |d<   |j	                  |       K |S )Nr   s3_path)copyr   r   r   r   filelist
key_prefixr"   	fileentryretentrys        r-   calculate_s3_pathr8   >  sS    
C 	>># ggll:y7PQ

8	
 Jr/   c                 |    g }| D ]4  }|j                         }t        |d         |d<   |j                  |       6 |S )zReally, "calculate md5", but since AWS uses their own format, we'll just call
    it a "local etag". TODO optimization: only calculate if remote key exists.r   
local_etag)r2   r   r   r3   s        r-   calculate_local_etagr;   H  sK     C 	>>#!9)J:O!P

8	
 Jr/   c                 4   g }| D ]  }|j                         }|d   }t        j                  j                  |      d   }|r|j	                  |      r	||   |d<   n t        j                  |d      \  |d<   |d<   |d   sd|d<   |j                  |        |S )Nr      	mime_typeF)strictencodingzapplication/octet-stream)r2   r   r   splitextget	mimetypes
guess_typer   )r4   override_mapr"   r6   r7   	localfilefile_extensions          r-   determine_mimetypesrH   T  s    
C 	>>#j)	 )))4Q7L,,^<$0$@H[! ;D:N:Nyaf:g7H[!8J#7 $$>H[!

8#& Jr/   c                     g }|D ]=  }|j                         }	 | j                  ||d         |d<   |j                  |       ? |S # t        ddg      $ r Y 'w xY w)Nr1   )BucketKeys3_head404403)r2   head_objectr   r   )s3buckets3keysretkeysentryr7   s         r-   head_s3rU   l  st    G !::<	"$..E)DT."UHY
 	x ! N #E5>2 		s   AAAc           	      d   t        |      }|D ]  }||d<   	 |dk(  st        | ||      }|dk(  r.|D ](  }|j                  d      r|d   d   |d   k(  rd|d<   ()* n|d	k(  r|D ]  }|j                  d      r|d
   }|d   }|d   d   }	|	t        j                  dddt	        j
                               z
  }
|
j                  |
j                  dz  z   }|d   d   }| d| |d<   | d| |d<   ||k  s||k(  sd|d<   d|d<    n	 |D cg c]  }|j                  d      r| c}S c c}w )N	_strategyforcechecksumrL   ETagr:   T	skip_flag	date_sizer   r   LastModifiedi  r=   )tzinfoiQ ContentLengthz / whytimewhysizez
no s3_headwhy)listrU   rB   datetimer   tzutcsecondsdays)rP   rQ   
s3fileliststrategykeeplisterT   local_modified_epoch
local_sizeremote_modified_datetimedeltaremote_modified_epochremote_sizer,   s                 r-   filter_listrr   z  s   JH "!+" w2vz2 : 
	Eyy##F+u\/BB)-E+& 
	 
[	  	,Eyy#',-='>$"7^
 ,1+;N+K(083D3DT1aXZX`X`Xb3cc(-e9K(L%#I.?&:%;3?T>U#Vi &0\[M#Bi '+@@ZS^E^)-E+&+e)	,. 	  :!quu['9A:::s   D-&D-c           	         g }|D ]  }d|d   i}|j                  d      r|d   |d<   |j                  d      r|d   |d<   |j                  d      r|d   |d<   | j                  |d	   ||d
   |d d        |j                  |        |S )NContentTyper>   
permissionACLcache_controlCacheControlstorage_classStorageClassr   r1   )	ExtraArgsCallbackConfig)rB   upload_filer   )rP   rQ   r4   paramsr"   rT   argss          r-   upload_filesr     s    
C 
u[12::l# .DK::o&#)/#:D ::o&#)/#:D 
uZ(&%	2Bd]ajno

5
 Jr/   c           
         |j                  d      }|j                  d      }| j                  d      }t        d |j                  ||      j	                         j                  dg       D              }t        d |D              }t        ||z
        }t        dt        |      d	      D 	cg c]
  }	||	|	d	z     }
}	|
D ]'  }| j                  |d
|D cg c]  }d|i c}i       ) |S c c}	w c c}w )NrQ   r5   list_objects_v2c              3   &   K   | ]	  }|d      yw)rK   N ).0r,   s     r-   	<genexpr>zremove_files.<locals>.<genexpr>  s      %s   )rJ   PrefixContentsc              3   8   K   | ]  }t        |d            yw)r1   Nr   )r   source_files     r-   r   zremove_files.<locals>.<genexpr>  s     RGK	23Rs   r   i  ObjectsrK   )rJ   Delete)	rB   get_paginatorsetpaginatebuild_full_resultrc   rangelendelete_objects)rP   
sourcelistr   rQ   r5   	paginatorcurrent_keys	keep_keysdelete_keysigroups_of_keyskeyskeys                r-   remove_filesr     s   ZZ!FL)J  !23I #,,F:,N``bffgqsuv L RzRRI|i/0K 8=QK@PRV7WX!k!AH-XNX ]
UY<ZceS\<Z0[\] 	 Y<Zs   -C,C1c                     t        t        dgd      t        g dd      t        d      t        ddd	      t        dd
      t        dg d      t        dd      t        dd      t        dd      t        dd      t        ddd      t        ddg d            } t        |       }t        s|j                  d       i }|j                  d   }	 |j                  d      }|dk(  rA	 t        |j                  d   |j                  d   |j                  d    !      |d"<   t        |d"   |j                  j                  d#            |d$<   t        |d$   |j                  d%         |d&<   	 t        |d&         |d'<   t%        |j                  d+   |d'   |j                  d(         |d,<   t'        ||j                  d+   |d,   |j                        |d-<   |j                  d.   rt)        ||d'   |j                        |d/<   |j                  d-      s|j                  d/      rd|d0<    |j*                  d2i | y # t        j                  j                  t        j                  j                  f$ r}|j                  |d       Y d }~d }~ww xY w# t         $ rE}|j                  d(   d)k(  r|j                  |d*       |d&   j#                         |d'<   Y d }~`d }~ww xY w# t        j                  j                  t        j                  j                  f$ r}|j                  |d1       Y d }~d }~ww xY w)3Npush)choicesdefault)rX   r\   rY   r\   T)requiredF )r   r   no_logr   )r   type)privatezpublic-readzpublic-read-writezauthenticated-readzaws-exec-readzbucket-owner-readzbucket-owner-full-control)r   r   dictz.*)r   r   *bool)r   r   r   STANDARD)r   REDUCED_REDUNDANCYSTANDARD_IA
ONEZONE_IAINTELLIGENT_TIERINGGLACIERDEEP_ARCHIVEOUTPOSTS)r   r   r   )modefile_change_strategyrQ   r5   	file_rootru   mime_mapr!   r    rw   deletery   )argument_specz!dateutil required for this module)msgr   rP   zFailed to connect to AWSr   r!   r    )r!   r    filelist_initialr   filelist_typedr5   filelist_s3filelist_local_etagr   rY   zhUnable to calculate checksum.  If running in FIPS mode, you may need to use another file_change_strategyrQ   filelist_actionableuploadsr   removedchangedzFailed to push filer   )r   AnsibleAWSModuleHAS_DATEUTIL	fail_jsonr   clientbotocore
exceptionsClientErrorBotoCoreErrorfail_json_awsr.   rH   rB   r8   r;   
ValueErrorr2   rr   r   r   	exit_json)r   moduleresultr   rP   rk   s         r-   mainr     sC   6(F3!*LVabT"5A62
 u62eT2eS1E26U?	
/&MP #F @AF== D@]]4  v~	?)5k*FMM)4LV\VcVcdmVn*F%& (;6BT;UW]WdWdWhWhisWt'uF#$$5f=M6NPVP]P]^jPk$lF=!M0DVMEZ0[,- -8FMM(+V4I-JFMMZpLq-F() !-Rx1H&QfJgioiviv wF9}}X&$0V<Q5RTZTaTa$by! zz)$

9(=$(y!
 FvE ++X-@-@-N-NO @Q$>??@  M==!78JF(( C 17}0E0J0J0L,-M& ##//1D1D1R1RS 	?  (= >>	?s\   I 3BK/ 6J B,K/ 7J=JJ	K,':K'!K/ 'K,,K/ /7M&L??M__main__)NN)r   )!DOCUMENTATIONEXAMPLESRETURNrd   r   rC   r   r   r   dateutilr   r   ImportErrorr   ansible.module_utils._textr   <ansible_collections.amazon.aws.plugins.module_utils.botocorer   ;ansible_collections.community.aws.plugins.module_utils.etagr   >ansible_collections.community.aws.plugins.module_utils.modulesr   r   r.   r8   r;   rH   rU   rr   r   r   r   __name__r   r/   r-   <module>r      s   sjBI
V    	 L	 / \ ` x9x	02;j $Wt zF s	  L
  		s"   A- A: -A76A7:BB