ó
è?F[c           @   sK  d  d l  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  d l Z d  d l	 m
 Z
 m Z m Z m Z m Z d  d l m Z m Z m Z e ƒ  Z e j d ƒ Z d e f d „  ƒ  YZ d e f d	 „  ƒ  YZ d
 e j f d „  ƒ  YZ d e f d „  ƒ  YZ d e f d „  ƒ  YZ d e f d „  ƒ  YZ d S(   iÿÿÿÿN(   t   Queue(   t   DEFAULT_PART_SIZEt   minimum_part_sizet   chunk_hashest	   tree_hasht   bytes_to_hex(   t   UploadArchiveErrort   DownloadArchiveErrort   TreeHashDoesNotMatchErrors   boto.glacier.concurrentt   ConcurrentTransfererc           B   s2   e  Z e d  d „ Z d „  Z d „  Z d „  Z RS(   i
   c         C   s   | |  _  | |  _ g  |  _ d  S(   N(   t
   _part_sizet   _num_threadst   _threads(   t   selft	   part_sizet   num_threads(    (    s6   lib/python2.7/site-packages/boto/glacier/concurrent.pyt   __init__+   s    		c         C   sl   t  | ƒ } |  j | k r' |  j } n | } t j d |  j | ƒ t t j | t | ƒ ƒ ƒ } | | f S(   Nsf   The part size specified (%s) is smaller than the minimum required part size.  Using a part size of: %s(   R   R
   t   logt   debugt   intt   matht   ceilt   float(   R   t
   total_sizet   min_part_size_requiredR   t   total_parts(    (    s6   lib/python2.7/site-packages/boto/glacier/concurrent.pyt   _calculate_required_part_size0   s    	c         C   sY   t  j d ƒ x |  j D] } t | _ q Wx |  j D] } | j ƒ  q4 Wt  j d ƒ d  S(   Ns   Shutting down threads.s   Threads have exited.(   R   R   R   t   Falset   should_continuet   join(   R   t   thread(    (    s6   lib/python2.7/site-packages/boto/glacier/concurrent.pyt   _shutdown_threads<   s    c         C   sb   t  j d ƒ x' t | ƒ D] } | j | | f ƒ q Wx$ t |  j ƒ D] } | j t ƒ qG Wd  S(   Ns   Adding work items to queue.(   R   R   t   ranget   putR   t   _END_SENTINEL(   R   R   t   worker_queueR   t   i(    (    s6   lib/python2.7/site-packages/boto/glacier/concurrent.pyt   _add_work_items_to_queueD   s
    (   t   __name__t
   __module__R   R   R   R   R%   (    (    (    s6   lib/python2.7/site-packages/boto/glacier/concurrent.pyR	   *   s   		t   ConcurrentUploaderc           B   s;   e  Z d  Z e d d „ Z d d „ Z d „  Z d „  Z RS(   s  Concurrently upload an archive to glacier.

    This class uses a thread pool to concurrently upload an archive
    to glacier using the multipart upload API.

    The threadpool is completely managed by this class and is
    transparent to the users of this class.

    i
   c         C   s/   t  t |  ƒ j | | ƒ | |  _ | |  _ d S(   su  
        :type api: :class:`boto.glacier.layer1.Layer1`
        :param api: A layer1 glacier object.

        :type vault_name: str
        :param vault_name: The name of the vault.

        :type part_size: int
        :param part_size: The size, in bytes, of the chunks to use when uploading
            the archive parts.  The part size must be a megabyte multiplied by
            a power of two.

        :type num_threads: int
        :param num_threads: The number of threads to spawn for the thread pool.
            The number of threads will control how much parts are being
            concurrently uploaded.

        N(   t   superR(   R   t   _apit   _vault_name(   R   t   apit
   vault_nameR   R   (    (    s6   lib/python2.7/site-packages/boto/glacier/concurrent.pyR   V   s    	c         C   s6  t  j | ƒ j } |  j | ƒ \ } } d g | } t ƒ  } t ƒ  } |  j j |  j | | ƒ }	 |	 d }
 |  j	 | | | ƒ |  j
 | |
 | | ƒ y |  j | | | ƒ Wn< t k
 ré } t j d ƒ |  j j |  j |
 ƒ | ‚ n Xt j d ƒ |  j j |  j |
 t t | ƒ ƒ | ƒ }	 t j d ƒ |	 d S(   s^  Concurrently create an archive.

        The part_size value specified when the class was constructed
        will be used *unless* it is smaller than the minimum required
        part size needed for the size of the given file.  In that case,
        the part size used will be the minimum part size required
        to properly upload the given file.

        :type file: str
        :param file: The filename to upload

        :type description: str
        :param description: The description of the archive.

        :rtype: str
        :return: The archive id of the newly created archive.

        t   UploadIdsH   An error occurred while uploading an archive, aborting multipart upload.s   Completing upload.s   Upload finished.t	   ArchiveIdN(   t   ost   statt   st_sizeR   t   NoneR    R*   t   initiate_multipart_uploadR+   R%   t   _start_upload_threadst   _wait_for_upload_threadsR   R   R   t   abort_multipart_uploadt   complete_multipart_uploadR   R   (   R   t   filenamet   descriptionR   R   R   t   hash_chunksR#   t   result_queuet   responset	   upload_idt   e(    (    s6   lib/python2.7/site-packages/boto/glacier/concurrent.pyt   uploadn   s2    			


		c         C   sƒ   xr t  | ƒ D]d } | j ƒ  } t | t ƒ r[ t j d | ƒ |  j ƒ  t d | ƒ ‚ n  | \ } } | | | <q W|  j ƒ  d  S(   Ns?   An error was found in the result queue, terminating threads: %ss0   An error occurred while uploading an archive: %s(   R    t   gett
   isinstancet	   ExceptionR   R   R   R   (   R   R;   R<   R   t   _t   resultt   part_numbert   tree_sha256(    (    s6   lib/python2.7/site-packages/boto/glacier/concurrent.pyR6       s    	
c         C   ss   t  j d ƒ x_ t |  j ƒ D]N } t |  j |  j | | | | ƒ } t j d ƒ | j	 ƒ  |  j
 j | ƒ q Wd  S(   Ns   Starting threads.gš™™™™™É?(   R   R   R    R   t   UploadWorkerThreadR*   R+   t   timet   sleept   startR   t   append(   R   R<   R>   R#   R9   RD   R   (    (    s6   lib/python2.7/site-packages/boto/glacier/concurrent.pyR5   °   s    
N(	   R&   R'   t   __doc__R   R   R3   R@   R6   R5   (    (    (    s6   lib/python2.7/site-packages/boto/glacier/concurrent.pyR(   L   s   	2	t   TransferThreadc           B   s,   e  Z d  „  Z d „  Z d „  Z d „  Z RS(   c         C   s2   t  t |  ƒ j ƒ  | |  _ | |  _ t |  _ d  S(   N(   R)   RN   R   t   _worker_queuet   _result_queuet   TrueR   (   R   R#   R<   (    (    s6   lib/python2.7/site-packages/boto/glacier/concurrent.pyR   ¼   s    		c         C   s‡   xv |  j  rx y |  j j d d ƒ } Wn t k
 r; q n X| t k rV |  j ƒ  d  S|  j | ƒ } |  j j | ƒ q W|  j ƒ  d  S(   Nt   timeouti   (	   R   RO   RA   t   EmptyR"   t   _cleanupt   _process_chunkRP   R!   (   R   t   workRE   (    (    s6   lib/python2.7/site-packages/boto/glacier/concurrent.pyt   runÄ   s    
c         C   s   d  S(   N(    (   R   RV   (    (    s6   lib/python2.7/site-packages/boto/glacier/concurrent.pyRU   Ñ   s    c         C   s   d  S(   N(    (   R   (    (    s6   lib/python2.7/site-packages/boto/glacier/concurrent.pyRT   Ô   s    (   R&   R'   R   RW   RU   RT   (    (    (    s6   lib/python2.7/site-packages/boto/glacier/concurrent.pyRN   »   s   			RH   c           B   s5   e  Z d  d  e d „ Z d „  Z d „  Z d „  Z RS(   i   c
   
      C   sn   t  t |  ƒ j | | ƒ | |  _ | |  _ | |  _ t | d ƒ |  _ | |  _ | |  _	 | |  _
 |	 |  _ d  S(   Nt   rb(   R)   RH   R   R*   R+   t	   _filenamet   opent   _fileobjt
   _upload_idt   _num_retriest   _time_between_retriest   _retry_exceptions(
   R   R,   R-   R9   R>   R#   R<   t   num_retriest   time_between_retriest   retry_exceptions(    (    s6   lib/python2.7/site-packages/boto/glacier/concurrent.pyR   Ù   s    						c      
   C   s¥   d  } x˜ t |  j d ƒ D]ƒ } y |  j | ƒ } PWq |  j k
 rœ } t j d | d |  j | d |  j d |  j | j	 | ƒ t
 j |  j ƒ | } q Xq W| S(   Ni   sp   Exception caught uploading part number %s for vault %s, attempt: (%s / %s), filename: %s, exception: %s, msg: %si    (   R3   R    R]   t   _upload_chunkR_   R   t   errorR+   RY   t	   __class__RI   RJ   R^   (   R   RV   RE   R$   R?   (    (    s6   lib/python2.7/site-packages/boto/glacier/concurrent.pyRU   ç   s    	c   
      C   sÍ   | \ } } | | } |  j  j | ƒ |  j  j | ƒ } t j | ƒ j ƒ  } t t | ƒ ƒ } | | t | ƒ d f } t	 j
 d | | ƒ |  j j |  j |  j | t | ƒ | | ƒ }	 |	 j ƒ  | | f S(   Ni   s   Uploading chunk %s of size %s(   R[   t   seekt   readt   hashlibt   sha256t	   hexdigestR   R   t   lenR   R   R*   t   upload_partR+   R\   R   (
   R   RV   RF   R   t
   start_bytet   contentst   linear_hasht   tree_hash_bytest
   byte_rangeR=   (    (    s6   lib/python2.7/site-packages/boto/glacier/concurrent.pyRc   ÷   s    
	
c         C   s   |  j  j ƒ  d  S(   N(   R[   t   close(   R   (    (    s6   lib/python2.7/site-packages/boto/glacier/concurrent.pyRT     s    (   R&   R'   RC   R   RU   Rc   RT   (    (    (    s6   lib/python2.7/site-packages/boto/glacier/concurrent.pyRH   Ø   s   		t   ConcurrentDownloaderc           B   s8   e  Z d  Z e d d „ Z d „  Z d „  Z d „  Z RS(   sý   
    Concurrently download an archive from glacier.

    This class uses a thread pool to concurrently download an archive
    from glacier.

    The threadpool is completely managed by this class and is
    transparent to the users of this class.

    i
   c         C   s&   t  t |  ƒ j | | ƒ | |  _ d S(   s  
        :param job: A layer2 job object for archive retrieval object.

        :param part_size: The size, in bytes, of the chunks to use when uploading
            the archive parts.  The part size must be a megabyte multiplied by
            a power of two.

        N(   R)   Rs   R   t   _job(   R   t   jobR   R   (    (    s6   lib/python2.7/site-packages/boto/glacier/concurrent.pyR     s    
c         C   sª   |  j  j } |  j | ƒ \ } } t ƒ  } t ƒ  } |  j | | | ƒ |  j | | ƒ y |  j | | | ƒ Wn) t k
 r˜ } t j	 d | ƒ | ‚ n Xt j	 d ƒ d S(   s’   
        Concurrently download an archive.

        :param filename: The filename to download the archive to
        :type filename: str

        s2   An error occurred while downloading an archive: %ss   Download completed.N(
   Rt   t   archive_sizeR   R    R%   t   _start_download_threadst   _wait_for_download_threadsR   R   R   (   R   R9   R   R   R   R#   R<   R?   (    (    s6   lib/python2.7/site-packages/boto/glacier/concurrent.pyt   download$  s    		
c      	   C   sB  d g | } t | d ƒ ° } x¦ t | ƒ D]˜ } | j ƒ  } t | t ƒ rz t j d | ƒ |  j ƒ  t	 d | ƒ ‚ n  | \ } }	 }
 } |
 | | <| |	 } | j
 | ƒ | j | ƒ | j ƒ  q, WWd QXt t | ƒ ƒ } t j d |  j j | ƒ |  j j | k r4|  j ƒ  t d |  j j | f ƒ ‚ n  |  j ƒ  d S(   s  
        Waits until the result_queue is filled with all the downloaded parts
        This indicates that all part downloads have completed

        Saves downloaded parts into filename

        :param filename:
        :param result_queue:
        :param total_parts:
        t   wbs?   An error was found in the result queue, terminating threads: %ss0   An error occurred while uploading an archive: %sNs?   Verifying final tree hash of archive, expecting: %s, actual: %ssB   Tree hash for entire archive does not match, expected: %s, got: %s(   R3   RZ   R    RA   RB   RC   R   R   R   R   Rf   t   writet   flushR   R   Rt   t   sha256_treehashR   (   R   R9   R<   R   R;   t   fRD   RE   RF   R   t   actual_hasht   dataRm   t
   final_hash(    (    s6   lib/python2.7/site-packages/boto/glacier/concurrent.pyRx   9  s6    	


	
	c         C   sg   t  j d ƒ xS t |  j ƒ D]B } t |  j | | ƒ } t j d ƒ | j ƒ  |  j	 j
 | ƒ q Wd  S(   Ns   Starting threads.gš™™™™™É?(   R   R   R    R   t   DownloadWorkerThreadRt   RI   RJ   RK   R   RL   (   R   R<   R#   RD   R   (    (    s6   lib/python2.7/site-packages/boto/glacier/concurrent.pyRw   `  s    
(   R&   R'   RM   R   R   Ry   Rx   Rw   (    (    (    s6   lib/python2.7/site-packages/boto/glacier/concurrent.pyRs     s   
		'R‚   c           B   s,   e  Z d  d  e d „ Z d „  Z d „  Z RS(   i   c         C   sA   t  t |  ƒ j | | ƒ | |  _ | |  _ | |  _ | |  _ d S(   s  
        Individual download thread that will download parts of the file from Glacier. Parts
        to download stored in work queue.

        Parts download to a temp dir with each part a separate file

        :param job: Glacier job object
        :param work_queue: A queue of tuples which include the part_number and
            part_size
        :param result_queue: A priority queue of tuples which include the
            part_number and the path to the temp file that holds that
            part's data.

        N(   R)   R‚   R   Rt   R]   R^   R_   (   R   Ru   R#   R<   R`   Ra   Rb   (    (    s6   lib/python2.7/site-packages/boto/glacier/concurrent.pyR   j  s
    			c         C   s   d } xt t |  j ƒ D]c } y |  j | ƒ } PWq |  j k
 rx } t j d | d |  j ƒ t j	 |  j
 ƒ | } q Xq W| S(   s‹   
        Attempt to download a part of the archive from Glacier
        Store the result in the result_queue

        :param work:
        s6   Exception caught downloading part number %s for job %si    N(   R3   R    R]   t   _download_chunkR_   R   Rd   Rt   RI   RJ   R^   (   R   RV   RE   RD   R?   (    (    s6   lib/python2.7/site-packages/boto/glacier/concurrent.pyRU   ƒ  s    	c   	      C   s¼   | \ } } | | } | | | d f } t  j d | | ƒ |  j j | ƒ } | j ƒ  } t t t | ƒ ƒ ƒ } | d | k r£ t d | | d | f ƒ ‚ n  | | t	 j
 | ƒ | f S(   s§   
        Downloads a chunk of archive from Glacier. Saves the data to a temp file
        Returns the part number and temp file location

        :param work:
        i   s   Downloading chunk %s of size %st   TreeHashsB   Tree hash for part number %s does not match, expected: %s, got: %s(   R   R   Rt   t
   get_outputRg   R   R   R   R   t   binasciit	   unhexlify(	   R   RV   RF   R   Rm   Rq   R=   R€   R   (    (    s6   lib/python2.7/site-packages/boto/glacier/concurrent.pyRƒ   –  s    

(   R&   R'   RC   R   RU   Rƒ   (    (    (    s6   lib/python2.7/site-packages/boto/glacier/concurrent.pyR‚   i  s
   	(   R0   R   t	   threadingRh   RI   t   loggingt   boto.compatR    R†   t   boto.glacier.utilsR   R   R   R   R   t   boto.glacier.exceptionsR   R   R   t   objectR"   t	   getLoggerR   R	   R(   t   ThreadRN   RH   Rs   R‚   (    (    (    s6   lib/python2.7/site-packages/boto/glacier/concurrent.pyt   <module>   s"   (	"o4]