U
    dR1                     @   s   d dl Z d dlZd dlmZ d dlmZ d dlmZmZm	Z	 d dl
mZmZmZmZ d dlmZ G dd deZG d	d
 d
eZG dd deZG dd dee	ZG dd deZG dd deZdS )    N)Redis)SentinelCommands)
ConnectionConnectionPoolSSLConnection)ConnectionErrorReadOnlyErrorResponseErrorTimeoutError)str_if_bytesc                   @   s   e Zd ZdS )MasterNotFoundErrorN__name__
__module____qualname__ r   r   2/tmp/pip-unpacked-wheel-xj5sloo9/redis/sentinel.pyr      s   r   c                   @   s   e Zd ZdS )SlaveNotFoundErrorNr   r   r   r   r   r      s   r   c                       sN   e Zd Z fddZdd Z fddZdd Zd	d
 Zd fdd	Z  Z	S )SentinelManagedConnectionc                    s   | d| _t jf | d S )Nconnection_pool)popr   super__init__)selfkwargs	__class__r   r   r      s    z"SentinelManagedConnection.__init__c                 C   sD   | j }t| j d|j d}| jr@d| j d| j }|| }|S )N	<service=z%s>z,host=z,port=)r   typer   service_namehostport)r   poolsZ	host_infor   r   r   __repr__   s    z"SentinelManagedConnection.__repr__c                    sD   |\| _ | _t   | jjr@| d t|  dkr@t	dd S )NZPINGZPONGzPING failed)
r    r!   r   connectr   check_connectionZsend_commandr   read_responser   )r   addressr   r   r   
connect_to    s    

z$SentinelManagedConnection.connect_toc              	   C   sf   | j r
d S | jjr$| | j  n>| j D ].}z| |W   S  tk
rZ   Y q.Y q.X q.td S N)_sockr   	is_masterr)   get_master_addressrotate_slavesr   r   )r   slaver   r   r   _connect_retry(   s    z(SentinelManagedConnection._connect_retryc                 C   s   | j | jdd S )Nc                 S   s   d S r*   r   )errorr   r   r   <lambda>6       z3SentinelManagedConnection.connect.<locals>.<lambda>)retryZcall_with_retryr0   r   r   r   r   r%   5   s    z!SentinelManagedConnection.connectFc                    sD   zt  j|dW S  tk
r>   | jjr8|   td Y nX d S )N)disable_decodingz"The previous master is now a slave)r   r'   r   r   r,   
disconnectr   )r   r6   r   r   r   r'   8   s    z'SentinelManagedConnection.read_response)F)
r   r   r   r   r$   r)   r0   r%   r'   __classcell__r   r   r   r   r      s   r   c                   @   s   e Zd ZdS )SentinelManagedSSLConnectionNr   r   r   r   r   r9   G   s   r9   c                       sP   e Zd ZdZ fddZdd Z fddZ fdd	Zd
d Zdd Z	  Z
S )SentinelConnectionPoolz
    Sentinel backed connection pool.

    If ``check_connection`` flag is set to True, SentinelManagedConnection
    sends a PING command right after establishing the connection.
    c                    sj   | d|ddrtnt|d< |dd| _|dd| _t jf | t	| | j
d< || _|| _d S )NZconnection_classsslFr,   Tr&   r   )getr   r9   r   r,   r&   r   r   weakrefproxyconnection_kwargsr   sentinel_manager)r   r   r@   r   r   r   r   r   S   s    
zSentinelConnectionPool.__init__c                 C   s,   | j r
dnd}t| j d| j d| dS )NZmasterr/   r   ())r,   r   r   r   )r   roler   r   r   r$   a   s    zSentinelConnectionPool.__repr__c                    s   t    d | _d | _d S r*   )r   resetmaster_addressslave_rr_counterr5   r   r   r   rD   e   s    
zSentinelConnectionPool.resetc                    s4   | j  p| j o| j|j|jfk}t }|o2||S r*   )r,   rE   r    r!   r   owns_connection)r   
connectioncheckparentr   r   r   rG   j   s
    z&SentinelConnectionPool.owns_connectionc                 C   s4   | j | j}| jr0| j|kr0|| _| jdd |S )NF)Zinuse_connections)r@   discover_masterr   r,   rE   r7   )r   rE   r   r   r   r-   q   s    
z)SentinelConnectionPool.get_master_addressc                 c   s   | j | j}|rh| jdkr2tdt|d | _tt|D ](}| jd t| | _|| j }|V  q>z|  V  W n t	k
r   Y nX t
d| jdS )zRound-robin slave balancerNr      zNo slave found for )r@   discover_slavesr   rF   randomrandintlenranger-   r   r   )r   slaves_r/   r   r   r   r.   {   s    

z$SentinelConnectionPool.rotate_slaves)r   r   r   __doc__r   r$   rD   rG   r-   r.   r8   r   r   r   r   r:   K   s   
r:   c                   @   sf   e Zd ZdZdddZdd Zdd	 Zd
d Zdd Zdd Z	dd Z
eefddZeefddZdS )Sentinelar  
    Redis Sentinel cluster client

    >>> from redis.sentinel import Sentinel
    >>> sentinel = Sentinel([('localhost', 26379)], socket_timeout=0.1)
    >>> master = sentinel.master_for('mymaster', socket_timeout=0.1)
    >>> master.set('foo', 'bar')
    >>> slave = sentinel.slave_for('mymaster', socket_timeout=0.1)
    >>> slave.get('foo')
    b'bar'

    ``sentinels`` is a list of sentinel nodes. Each node is represented by
    a pair (hostname, port).

    ``min_other_sentinels`` defined a minimum number of peers for a sentinel.
    When querying a sentinel, if it doesn't meet this threshold, responses
    from that sentinel won't be considered valid.

    ``sentinel_kwargs`` is a dictionary of connection arguments used when
    connecting to sentinel instances. Any argument that can be passed to
    a normal Redis connection can be specified here. If ``sentinel_kwargs`` is
    not specified, any socket_timeout and socket_keepalive options specified
    in ``connection_kwargs`` will be used.

    ``connection_kwargs`` are keyword arguments that will be used when
    establishing a connection to a Redis server.
    r   Nc                    sD   |d krdd |  D }| _ fdd|D  _| _| _d S )Nc                 S   s    i | ]\}}| d r||qS )Zsocket_)
startswith).0kvr   r   r   
<dictcomp>   s    
  z%Sentinel.__init__.<locals>.<dictcomp>c                    s    g | ]\}}t ||f jqS r   )r   sentinel_kwargs)rW   hostnamer!   r5   r   r   
<listcomp>   s   z%Sentinel.__init__.<locals>.<listcomp>)itemsr[   	sentinelsmin_other_sentinelsr?   )r   r_   r`   r[   r?   r   r5   r   r      s    	
zSentinel.__init__c                 O   s\   t |dd}d| kr&|d |r@t| jj|| n| jD ]}|j|| qFdS )z
        Execute Sentinel command in sentinel nodes.
        once - If set to True, then execute the resulting command on a single
        node at random, rather than across the entire sentinel cluster.
        onceFT)boolr<   keysr   rN   choicer_   execute_command)r   argsr   ra   sentinelr   r   r   re      s    

zSentinel.execute_commandc                 C   s@   g }| j D ]}|d|jj q
t| j dd| dS )Nz{host}:{port}z<sentinels=[,z]>)r_   append
format_mapr   r?   r   r   join)r   Zsentinel_addressesrg   r   r   r   r$      s    
zSentinel.__repr__c                 C   s2   |d r|d s|d rdS |d | j k r.dS dS )Nr,   is_sdownis_odownFznum-other-sentinelsT)r`   )r   stater   r   r   r   check_master_state   s
    zSentinel.check_master_statec              
   C   s   t | jD ]|\}}z| }W n ttfk
r:   Y q
Y nX ||}|r
| ||r
|| jd  | jd< | j|< |d |d f  S q
td|dS )z
        Asks sentinel servers for the Redis master's address corresponding
        to the service labeled ``service_name``.

        Returns a pair (address, port) or raises MasterNotFoundError if no
        master is found.
        r   ipr!   zNo master found for N)	enumerater_   Zsentinel_mastersr   r
   r<   ro   r   )r   r   Zsentinel_norg   Zmastersrn   r   r   r   rK      s    

zSentinel.discover_masterc                 C   s:   g }|D ],}|d s|d rq| |d |d f q|S )z1Remove slaves that are in an ODOWN or SDOWN staterm   rl   rp   r!   )ri   )r   rR   Zslaves_aliver/   r   r   r   filter_slaves   s    zSentinel.filter_slavesc                 C   sT   | j D ]H}z||}W n tttfk
r6   Y qY nX | |}|r|  S qg S )z;Returns a list of alive slaves for service ``service_name``)r_   Zsentinel_slavesr   r	   r
   rr   )r   r   rg   rR   r   r   r   rM     s    



zSentinel.discover_slavesc                 K   s0   d|d< t | j}|| |||| f|dS )a  
        Returns a redis client instance for the ``service_name`` master.

        A :py:class:`~redis.sentinel.SentinelConnectionPool` class is
        used to retrieve the master's address before establishing a new
        connection.

        NOTE: If the master's address has changed, any cached connections to
        the old master are closed.

        By default clients will be a :py:class:`~redis.Redis` instance.
        Specify a different class to the ``redis_class`` argument if you
        desire something different.

        The ``connection_pool_class`` specifies the connection pool to
        use.  The :py:class:`~redis.sentinel.SentinelConnectionPool`
        will be used by default.

        All other keyword arguments are merged with any connection_kwargs
        passed to this class and passed to the connection pool as keyword
        arguments to be used to initialize Redis connections.
        Tr,   r   dictr?   updater   r   Zredis_classZconnection_pool_classr   r?   r   r   r   
master_for  s    

 zSentinel.master_forc                 K   s0   d|d< t | j}|| |||| f|dS )a  
        Returns redis client instance for the ``service_name`` slave(s).

        A SentinelConnectionPool class is used to retrieve the slave's
        address before establishing a new connection.

        By default clients will be a :py:class:`~redis.Redis` instance.
        Specify a different class to the ``redis_class`` argument if you
        desire something different.

        The ``connection_pool_class`` specifies the connection pool to use.
        The SentinelConnectionPool will be used by default.

        All other keyword arguments are merged with any connection_kwargs
        passed to this class and passed to the connection pool as keyword
        arguments to be used to initialize Redis connections.
        Fr,   rs   rt   rw   r   r   r   	slave_for3  s    

 zSentinel.slave_for)r   N)r   r   r   rT   r   re   r$   ro   rK   rr   rM   r   r:   rx   ry   r   r   r   r   rU      s      
	
)rU   )rN   r=   Zredis.clientr   Zredis.commandsr   Zredis.connectionr   r   r   Zredis.exceptionsr   r   r	   r
   Zredis.utilsr   r   r   r   r9   r:   rU   r   r   r   r   <module>   s   4B