o
    <inE                     @   s  U d dl Z d dlZd dlZd dlmZmZ d dlmZ d dlm	Z	m
Z
mZmZmZ d dlmZ d dlmZ d dlmZ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" eeef Z#dede$fddZ%de&dede&fddZ'dZ(dZ)dZ*dZ+dZ,e-e.Z/G dd deZ0G dd deZ1G dd de1Z2G dd de2Z3G dd  d e2Z4G d!d" d"e2Z5G d#d$ d$eZ6e6j7Z8e6e9d%< G d&d' d'e0Z:G d(d) d)e:Z;G d*d+ d+e:Z<dS ),    N)ABCabstractmethod)Enum)ListOptionalTupleTypeUnion)Redis)RedisCluster)DEFAULT_TIMEOUTAsyncHTTPClientWrapper)	NoBackoff)
HttpClient)UnhealthyDatabaseException)Retryclsreturnc                 C   s$   t | j}tdd |j D S )z7Extract parameter names from a class's __init__ method.c                 s   s6    | ]\}}|d kr|j tjjtjjfv r|V  qdS )selfN)kindinspect	ParameterPOSITIONAL_OR_KEYWORDKEYWORD_ONLY).0nameparam r   `/root/parts/websockify/install/lib/python3.10/site-packages/redis/asyncio/multidb/healthcheck.py	<genexpr>   s    z#_get_init_params.<locals>.<genexpr>)r   	signature__init__	frozenset
parametersitems)r   sigr   r   r   _get_init_params   s   r&   kwargsc                    s   t |  fdd|  D S )zJFilter kwargs to only include parameters accepted by the class's __init__.c                    s   i | ]\}}| v r||qS r   r   )r   kvallowedr   r   
<dictcomp>(   s    z"_filter_kwargs.<locals>.<dictcomp>)r&   r$   )r'   r   r   r*   r   _filter_kwargs%   s   r-         g      ?i  c                   @   sh   e Zd ZdZeedefddZeedefddZ	eedefddZ
ed	edefd
dZdS )HealthCheckz!
    Health check interface.
    r   c                 C      dS )z*Number of probes to execute health checks.Nr   r   r   r   r   health_check_probes9      zHealthCheck.health_check_probesc                 C   r1   )z"Delay between health check probes.Nr   r2   r   r   r   health_check_delay?   r4   zHealthCheck.health_check_delayc                 C   r1   )zCTimeout for the full health check operation (including all probes).Nr   r2   r   r   r   health_check_timeoutE   r4   z HealthCheck.health_check_timeout	hc_clientc                       dS )ap  
        Function to determine the health status.

        Args:
            database: The database being checked
            hc_client: A Redis client (AsyncRedis or AsyncRedisCluster) to use for
                health checks. This client follows topology changes automatically.

        Returns:
            True if the database is healthy, False otherwise.
        Nr   r   databaser7   r   r   r   check_healthK   s   zHealthCheck.check_healthN)__name__
__module____qualname____doc__propertyr   intr3   floatr5   r6   AsyncRedisClientTboolr;   r   r   r   r   r0   4   s    r0   c                   @   s`   e Zd ZdZedee defddZededefddZ	ede
fd	d
ZedddZdS )HealthCheckPolicyz)
    Health checks execution policy.
    health_checksr   c                    r8   )z8Execute health checks and return database health status.Nr   )r   rF   r:   r   r   r   execute`      zHealthCheckPolicy.executehealth_checkc                    r8   z?
        Executes health check against given database.
        Nr   r   rI   r:   r   r   r   _executee      zHealthCheckPolicy._executec                    r8   )z=
        Get a health check client for the database.
        Nr   )r   r:   r   r   r   
get_clientl   rM   zHealthCheckPolicy.get_clientNc                    r8   )Close all health check clients.Nr   r2   r   r   r   closes   rH   zHealthCheckPolicy.closer   N)r<   r=   r>   r?   r   r   r0   rD   rG   rL   rC   rN   rP   r   r   r   r   rE   [   s    rE   c                   @   s\   e Zd ZdZdd Zdee defddZde	fdd	Z
dddZededefddZd
S )AbstractHealthCheckPolicyz'
    Abstract health check policy.
    c                 C   s
   i | _ d S N)_clientsr2   r   r   r   r!   ~   s   
z"AbstractHealthCheckPolicy.__init__rF   r   c                    sh   dt f fddtjfdd|D ddiI dH }|D ]}t|tr,td	 ||s1 d
S qdS )a  
        Execute all health checks concurrently with individual timeouts.
        Each health check runs with its own timeout, and all run in parallel.

        All exception handling is centralized here - _execute() methods just
        propagate exceptions naturally.
        rI   c                    s    t j|  | jdI d H S )N)timeout)asynciowait_forrL   r6   )rI   )r:   r   r   r   execute_with_timeout   s
   
z?AbstractHealthCheckPolicy.execute.<locals>.execute_with_timeoutc                    s   g | ]} |qS r   r   )r   hc)rX   r   r   
<listcomp>   s    z5AbstractHealthCheckPolicy.execute.<locals>.<listcomp>return_exceptionsTNzUnhealthy databaseF)r0   rV   gather
isinstance	Exceptionr   )r   rF   r:   resultsresultr   )r:   rX   r   r   rG      s   

z!AbstractHealthCheckPolicy.executec              	      s   t |}| j|}|du rvt|jttfr)|j }t|t}tdi |}nHt|jt	t
frg|j  }t|t	}|jj}|rc|d }t	d|j|j|jjj|jjj|jjj|jjd|}ntdtdt|j || j|< |S )a  
        Get or create a health check client for the database.

        Creates a single client instance per database that follows topology
        changes automatically. For cluster databases, the client handles
        node discovery and slot mapping internally.
        Nr   )hostportdynamic_startup_nodesaddress_remaprequire_full_coverageretryz?Cluster client has no nodes - cannot create health check clientzUnsupported client type: r   )idrT   getr]   client
AsyncRedis	SyncRedisget_connection_kwargsr-   AsyncRedisClusterSyncRedisClustercopystartup_nodesra   rb   nodes_manager_dynamic_startup_nodesrd   _require_full_coveragerf   
ValueError	TypeErrortype)r   r:   db_idri   conn_kwargsfiltered_kwargsrp   
first_noder   r   r   rN      s<   




z$AbstractHealthCheckPolicy.get_clientNc                    s>   dd | j  D }|rtj|ddiI dH  | j   dS )rO   c                 S   s   g | ]	}t | qS r   )rV   create_taskaclose)r   ri   r   r   r   rZ      s    z3AbstractHealthCheckPolicy.close.<locals>.<listcomp>r[   TN)rT   valuesrV   r\   clear)r   close_tasksr   r   r   rP      s   zAbstractHealthCheckPolicy.closerI   c                    r8   rJ   r   rK   r   r   r   rL      rM   z"AbstractHealthCheckPolicy._executerQ   )r<   r=   r>   r?   r!   r   r0   rD   rG   rC   rN   rP   r   rL   r   r   r   r   rR   y   s    !
-rR   c                   @   "   e Zd ZdZdedefddZdS )HealthyAllPolicyzM
    Policy that returns True if all health check probes are successful.
    rI   r   c                    sd   |  |I dH }|j}t|D ]}|||I dH }|s  dS ||d k r/t|jI dH  qdS )
        Executes health check against given database.

        Uses a single client that handles topology changes automatically.
        NF   T)rN   r3   ranger;   rV   sleepr5   )r   rI   r:   ri   probesattemptr`   r   r   r   rL      s   zHealthyAllPolicy._executeNr<   r=   r>   r?   r0   rD   rL   r   r   r   r   r          r   c                   @   r   )HealthyMajorityPolicya3  
    Policy that returns True if a majority of health check probes are successful.

    Majority means more than half must pass:
    - 3 probes: need 2+ to pass (1 failure allowed)
    - 4 probes: need 3+ to pass (1 failure allowed, tie = unhealthy)
    - 5 probes: need 3+ to pass (2 failures allowed)
    rI   r   c           
         s   |j }|d d }| |I dH }d}t|D ]I}z|||I dH }|s2|d8 }|dk r2W  dS W n tyQ }	 z|	}|d8 }|dk rG|W Y d}	~	nd}	~	ww ||d k rat|jI dH  qdS )r   r      Nr   FTr3   rN   r   r;   r^   rV   r   r5   )
r   rI   r:   r   allowed_unsuccessful_probesri   last_exceptionr   r`   er   r   r   rL     s2   zHealthyMajorityPolicy._executeNr   r   r   r   r   r      s    	r   c                   @   r   )HealthyAnyPolicyzT
    Policy that returns True if at least one health check probe is successful.
    rI   r   c           	         s   |j }d}| |I dH }t|D ]7}z|||I dH }|r$W  dS W n ty9 } z|}W Y d}~nd}~ww ||d k rIt|jI dH  q|rN|dS )r   NTr   Fr   )	r   rI   r:   r   r   ri   r   r`   r   r   r   r   rL   -  s(   zHealthyAnyPolicy._executeNr   r   r   r   r   r   (  r   r   c                   @   s   e Zd ZeZeZeZdS )HealthCheckPoliciesN)	r<   r=   r>   r   HEALTHY_ALLr   HEALTHY_MAJORITYr   HEALTHY_ANYr   r   r   r   r   J  s    r   DEFAULT_HEALTH_CHECK_POLICYc                   @   sv   e Zd ZeeefdededefddZe	defddZ
e	defd	d
Ze	defddZededefddZdS )AbstractHealthCheckr3   r5   r6   c                 C   s&   |dk rt d|| _|| _|| _d S )Nr   z*health_check_probes must be greater than 0)rt   _health_check_probes_health_check_delay_health_check_timeout)r   r3   r5   r6   r   r   r   r!   T  s
   
zAbstractHealthCheck.__init__r   c                 C      | j S rS   )r   r2   r   r   r   r3   `     z'AbstractHealthCheck.health_check_probesc                 C   r   rS   )r   r2   r   r   r   r5   d  r   z&AbstractHealthCheck.health_check_delayc                 C   r   rS   )r   r2   r   r   r   r6   h  r   z(AbstractHealthCheck.health_check_timeoutr7   c                    s   d S rS   r   r9   r   r   r   r;   l  s   z AbstractHealthCheck.check_healthN)r<   r=   r>   DEFAULT_HEALTH_CHECK_PROBESDEFAULT_HEALTH_CHECK_DELAYDEFAULT_HEALTH_CHECK_TIMEOUTrA   rB   r!   r@   r3   r5   r6   r   rC   rD   r;   r   r   r   r   r   S  s&    
r   c                   @   r   )PingHealthCheckz-
    Health check based on PING command.
    r7   r   c                    sJ   t |tr|dI d H S | }|D ]}|jdI d H s" dS qdS )NPINGFT)r]   rj   execute_command	get_nodesredis_connection)r   r:   r7   	all_nodesnoder   r   r   r;   v  s   
zPingHealthCheck.check_healthN)r<   r=   r>   r?   rC   rD   r;   r   r   r   r   r   q  r   r   c                       s   e Zd ZdZdeeddddddddeeefde	de	de
deeeef  d	ed
ee dee deeeef  dee dee dee de	de
de
f fddZdedefddZ  ZS )LagAwareHealthCheckz
    Health check available for Redis Enterprise deployments.
    Verify via REST API that the database is healthy based on different lags.
    i$  NTrest_api_portlag_aware_tolerancehttp_timeout
auth_basic
verify_tlsca_fileca_pathca_dataclient_cert_fileclient_key_fileclient_key_passwordr3   r5   r6   c                    sN   t t||tt dd|||||	|
|d
| _|| _|| _t j|||d dS )a  
        Initialize LagAwareHealthCheck with the specified parameters.

        Args:
            rest_api_port: Port number for Redis Enterprise REST API (default: 9443)
            lag_aware_tolerance: Tolerance in lag between databases in MS (default: 100)
            http_timeout: Request timeout in seconds (default: DEFAULT_TIMEOUT)
            auth_basic: Tuple of (username, password) for basic authentication
            verify_tls: Whether to verify TLS certificates (default: True)
            ca_file: Path to CA certificate file for TLS verification
            ca_path: Path to CA certificates directory for TLS verification
            ca_data: CA certificate data as string or bytes
            client_cert_file: Path to client certificate file for mutual TLS
            client_key_file: Path to client private key file for mutual TLS
            client_key_password: Password for encrypted client private key
        r   )retries)
rU   r   rf   r   r   r   r   r   r   r   )r3   r5   r6   N)	r   r   r   r   _http_client_rest_api_port_lag_aware_tolerancesuperr!   )r   r   r   r   r   r   r   r   r   r   r   r   r3   r5   r6   	__class__r   r   r!     s*   $
zLagAwareHealthCheck.__init__r7   r   c           
         s  |j du r
tdt|jttfr|j d }n|j d j}|j  d| j	 }|| j
j_d}| j
dI dH D ]"}|d D ]}|d |krM|} n|d	 D ]
}||kr[|} nqQqAq;|du rktd
 tdd|d  d| j }	| j
j|	ddI dH  dS )a  
        Check database health via Redis Enterprise REST API.

        Note: The client parameter is not used for this health check as it
        relies on the REST API instead of Redis protocol. The client is
        accepted for interface compatibility.
        Nz[Database health check url is not set. Please check DatabaseConfig for the current database.ra   r   :z/v1/bdbs	endpointsdns_nameaddrz8LagAwareHealthCheck failed: Couldn't find a matching bdbzCould not find a matching bdbz	/v1/bdbs/uidz=/availability?extend_check=lag&availability_lag_tolerance_ms=F)expect_jsonT)health_check_urlrt   r]   ri   rj   rk   rl   r   ra   r   r   base_urlrh   loggerwarningr   )
r   r:   r7   db_hostr   matching_bdbbdbendpointr   urlr   r   r   r;     s>   


z LagAwareHealthCheck.check_health)r<   r=   r>   r?   DEFAULT_LAG_AWARE_TOLERANCEr   r   r   r   rA   rB   r   r   strrD   r	   bytesr!   rC   r;   __classcell__r   r   r   r   r     s\    	
:r   )=rV   r   loggingabcr   r   enumr   typingr   r   r   r   r	   redis.asyncior
   rj   redis.asyncio.clusterr   rm   redis.asyncio.http.http_clientr   r   redis.backoffr   redis.clientrk   redis.clusterrn   redis.http.http_clientr   redis.multidb.exceptionr   redis.retryr   rC   r"   r&   dictr-   r   DEFAULT_HEALTH_CHECK_INTERVALr   r   r   	getLoggerr<   r   r0   rE   rR   r   r   r   r   r   r   __annotations__r   r   r   r   r   r   r   <module>   sF   
 
'j,"