o
    `j`~                     @   s   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Zd dlZd dl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mZmZ d dlmZ d dlmZ eeZG dd	 d	ZdddZdd Zdd ZdZ dd Z!dS )    N)errors)Future)WakeupNotifier)MetadataRequestMetadataResponseCoordinatorType)TopicPartition)ensure_valid_topic_namec                   @   s\  e Zd ZdZddg dddZdd Zed	d
 Zdd Zdd Z	dd Z
dd ZdRddZdd Zdd Zdd Zdd Zdd Zd d! Zd"d# Zd$d% Zd&d' Zd(d) Zd*d+ Zd,d- Zd.d/ Zd0d1 Zd2d3 Zejfd4d5Zd6d7 Zd8d9 Z d:d; Z!ed<d= Z"dSd>d?Z#d@dA Z$dBdC Z%dDdE Z&dFdG Z'dHdI Z(dJdK Z)dLdM Z*dSdNdOZ+dPdQ Z,dS )TClusterMetadataa*  
    A class to manage kafka cluster metadata.

    Keyword Arguments:
        retry_backoff_ms (int): Milliseconds to backoff when retrying on
            errors. Default: 100.
        metadata_max_age_ms (int): The period of time in milliseconds after
            which we force a refresh of metadata even if we haven't seen any
            partition leadership changes to proactively discover any new
            brokers or partitions. Default: 300000
        bootstrap_servers: 'host[:port]' string (or list of 'host[:port]'
            strings) that the client should contact to bootstrap initial
            cluster metadata. This does not have to be the full node list.
            It just needs to have at least one broker that will respond to a
            Metadata API Request. Default port is 9092. If no servers are
            specified, will default to localhost:9092.
        allow_auto_create_topics (bool): Enable/disable auto topic creation
            on metadata request. Only available with api_version >= (0, 11).
            Default: True
    d   i Tuse_all_dns_ips)retry_backoff_msmetadata_max_age_msbootstrap_serversallow_auto_create_topicsclient_dns_lookupc                 K   s   d | _ t | _i | _i | _tt| _i | _i | _	i | _
d| _d| _d| _d | _t | _t | _d| _t | _t | _d | _d | _d | _d | _d | _d| _t| j| _| jD ]}||v re|| | j|< qX|  | _ i | _!d S )Nr   TF)"_managerset_topics_brokers_partitionscollectionsdefaultdict_broker_partitions
_topic_ids_topic_names_by_id_coordinators_last_refresh_ms_last_successful_refresh_ms_need_update_future
_listeners	threadingLock_lockneed_all_topic_metadataunauthorized_topicsinternal_topics
controller
cluster_id_refresh_loop_future_refresh_future_wakeupclosedcopyDEFAULT_CONFIGconfig_generate_bootstrap_brokers_bootstrap_brokers_coordinator_brokers)selfconfigskey r7   K/home/djax/ivt_ai_plugin/venv/lib/python3.10/site-packages/kafka/cluster.py__init__3   s<   



zClusterMetadata.__init__c                 C   s   | j duo	| j j S )z True if a refresh is mid-flight.N)r+   is_doner4   r7   r7   r8   metadata_refresh_in_progressU   s   z,ClusterMetadata.metadata_refresh_in_progressc                 C   s   t || _t| jj| _dS )aj  Wire this cluster to its connection manager.

        Construction is split from attach so ClusterMetadata can be built
        standalone (tests, snapshots) without a live manager. The reference is
        held via weakref.proxy so that manager <-> cluster does not form a GC
        cycle; manager.close() still calls cluster.close() to clear eagerly.
        N)weakrefproxyr   r   _netr,   )r4   managerr7   r7   r8   attachZ   s   zClusterMetadata.attachc                 C   s   d| _ | j  d S )NT)r-   r,   notifyr;   r7   r7   r8   closee   s   zClusterMetadata.closec                 C   s4   | j du r	td| jdurdS | j | j| _dS )zOSpawn the periodic refresh coroutine. Idempotent. Triggers bootstrap if needed.N*start_refresh_loop requires prior attach())r   RuntimeErrorr*   	call_soon_refresh_loopr;   r7   r7   r8   start_refresh_loopi   s
   

z"ClusterMetadata.start_refresh_loopc              
      s  | j du r
td| j jstd | j  I dH  td | js| jr,| j	I dH  | 
 }|dkrPz	|  I dH  W n tjyN   tjddd Y nw q ztd	|d
  | |d
 I dH  W n ty| } ztd| W Y d}~nd}~ww | jr#td dS )zLAwaits ttl() then triggers refresh_metadata(); request_update() wakes early.NrD   z(Metadata refresh loop needs bootstrap...zStarting metadata refresh loopr   zMetadata refresh failedT)exc_infoz%Sleeping %s for next Metadata refresh  zMetadata refresh loop error: %szStopping metadata refresh loop)r   rE   bootstrappedlogdebugbootstrap_asyncinfor-   r<   r+   ttlrefresh_metadataErrors
KafkaErrorr,   	Exceptionerror)r4   ttl_msexcr7   r7   r8   rG   q   s6   


zClusterMetadata._refresh_loopNc              
      s   | j du r
td| jrtd | jI dH  dS t | _z
| |I dH  W n ty; } z| j	|  d}~ww | j
d dS )a  Send one MetadataRequest and apply the response.

        Concurrent callers share a single in-flight request: if a refresh is
        already underway, additional callers await the same Future and see the
        same outcome (success or exception). This avoids duplicate broker
        requests when bootstrap and the refresh loop race, or when external
        callers invoke refresh while the loop is mid-flight.
        Nz(refresh_metadata requires prior attach()z5Metadata refresh already in flight; awaiting existing)r   rE   r<   rL   rM   r+   r   _do_refresh_metadatarT   failuresuccess)r4   node_idrW   r7   r7   r8   rQ      s    
	
z ClusterMetadata.refresh_metadatac              
      s   |d u r
| j  n|}|d u r td | j d td| j d z|  }t	d|| | j 
||I d H }W n tyU } ztd| | |  d }~ww t	d| d | | d S )Nz6No node available for metadata refresh - backoff/retrymetadataz&Sending metadata request %s to node %szMetadata refresh: failed %sz#Metadata refresh: success (node_id=))r   least_loaded_noderL   warningupdate_backoffrR   NodeNotReadyErrorreset_backoffmetadata_requestrM   sendrT   rU   failed_updateupdate_metadata)r4   r[   requestresponserW   r7   r7   r8   rX      s&   


z$ClusterMetadata._do_refresh_metadatac                 C   s`   t | jd }| jd dkrt|}i }t|D ]\}\}}}d| }t|||d ||< q|S )Nr   r   (resolve_canonical_bootstrap_servers_onlyzbootstrap-%s)collect_hostsr0   #expand_to_canonical_bootstrap_hosts	enumerater   MetadataResponseBroker)r4   bootstrap_hostsbrokersihostport_r[   r7   r7   r8   r1      s   z+ClusterMetadata._generate_bootstrap_brokersc                 C   s
   || j v S N)r2   )r4   r[   r7   r7   r8   is_bootstrap   s   
zClusterMetadata.is_bootstrapc                 C   s@   |D ]}t | qt|| jst | S t|| _|  S )zSet specific topics to track for metadata.

        Arguments:
            topics (list of str): topics to check for metadata

        Returns:
            Future: resolves after metadata request/response
        )r	   r   
differencer   r   rZ   request_update)r4   topicstopicr7   r7   r8   
set_topics   s   	

zClusterMetadata.set_topicsc                 C   s2   t | || jv rt | S | j| |  S )ap  Add a topic to the list of topics tracked via metadata.

        Arguments:
            topic (str): topic to track

        Returns:
            Future: resolves after metadata request/response

        Raises:
            TypeError: if topic is not a string
            ValueError: if topic is invalid: must be chars (a-zA-Z0-9._-), and less than 250 length
        )r	   r   r   rZ   addrw   r4   ry   r7   r7   r8   	add_topic   s
   
zClusterMetadata.add_topicc                 C      t | j S )ziGet all MetadataResponseBroker

        Returns:
            list: [MetadataResponseBroker, ...]
        )listr   valuesr;   r7   r7   r8   ro      s   zClusterMetadata.brokersc                 C   r~   )zGet bootstrap brokers only, extracted from the
        bootstrap_servers config option. Node ids are synthesized
        as 'bootstrap-0' etc.

        Returns:
            list: [MetadataResponseBroker, ...]
        )r   r2   r   r;   r7   r7   r8   bootstrap_brokers   s   z!ClusterMetadata.bootstrap_brokersc                 C   s$   | j |p| j|p| j|S )zGet MetadataResponseBroker

        Arguments:
            broker_id (int or str): node_id for a broker to check

        Returns:
            MetadataResponseBroker or None if not found
        )r   getr2   r3   r4   	broker_idr7   r7   r8   broker_metadata   s
   


zClusterMetadata.broker_metadatac                 C   s    || j vrdS t| j |  S )zReturn set of all partitions for topic (whether available or not)

        Arguments:
            topic (str): topic to check for partitions

        Returns:
            set: {partition (int), ...}
            None if topic not found.
        N)r   r   keysr|   r7   r7   r8   partitions_for_topic  s   

z$ClusterMetadata.partitions_for_topicc                 C   s*   || j vrdS tdd | j |  D S )zReturn set of partitions with known leaders

        Arguments:
            topic (str): topic to check for partitions

        Returns:
            set: {partition (int), ...}
            None if topic not found.
        Nc                 S   s   g | ]\}}|j d kr|qS ))	leader_id).0	partitionr\   r7   r7   r8   
<listcomp>&  s    
zBClusterMetadata.available_partitions_for_topic.<locals>.<listcomp>)r   r   itemsr|   r7   r7   r8   available_partitions_for_topic  s
   

z.ClusterMetadata.available_partitions_for_topicc                 C   :   |j | jvrdS |j| j|j  vrdS | j|j  |j jS )z:Return node_id of leader, -1 unavailable, None if unknown.N)ry   r   r   r   r4   r   r7   r7   r8   leader_for_partition*  
   z$ClusterMetadata.leader_for_partitionc                 C   sX   |  |}|du rdS |j| jvrdS | j|j |j}|du r#dS ||jvr*dS |S )a  Return MetadataResponseBroker for ``node_id`` only when it is
        known AND still listed as a replica of ``partition`` (KIP-392).

        Used by the consumer's preferred-read-replica routing to avoid
        sending fetches to a broker that has been demoted out of the
        partition's replica set even though it still exists as a node.

        Arguments:
            partition (TopicPartition): topic / partition to look up.
            node_id (int): broker id to validate.

        Returns:
            MetadataResponseBroker if the node exists in cluster metadata
            and is currently listed as a replica of ``partition``;
            otherwise None.
        N)r   ry   r   r   r   replica_nodes)r4   r   r[   brokerpartition_datar7   r7   r8   is_replica_node2  s   

zClusterMetadata.is_replica_nodec                 C   r   )zIReturn leader_epoch for partition, or None if topic/partition is unknown.N)ry   r   r   leader_epochr   r7   r7   r8   leader_epoch_for_partitionO  r   z*ClusterMetadata.leader_epoch_for_partitionc                 C   s   | j [ | j|ji |j}|du r	 W d   dS |dk s&||jkr/	 W d   dS |j}||_||_||krU|| jv rI| j| | |dkrU| j| 	| 	 W d   dS 1 saw   Y  dS )a  Apply a KIP-951 current-leader hint from a Fetch/Produce response.

        The cached leader id and epoch for ``partition`` are replaced only when
        ``leader_epoch`` is strictly newer than the cached value (and
        non-negative). When the leader id moves, ``_broker_partitions`` is
        rewired so leader-based routing follows immediately.

        Arguments:
            partition (TopicPartition): topic / partition the hint is about.
            leader_id (int): broker id named as the new leader.
            leader_epoch (int): epoch of that new leader.

        Returns:
            bool: True iff cached state was changed.
        NFr   r   T)
r$   r   r   ry   r   r   r   r   discardr{   )r4   r   r   r   p_data
old_leaderr7   r7   r8   update_partition_leaderW  s$   
$z'ClusterMetadata.update_partition_leaderc                 C      | j |S )a  Return TopicPartitions for which the broker is a leader.

        Arguments:
            broker_id (int or str): node id for a broker

        Returns:
            set: {TopicPartition, ...}
            None if the broker either has no partitions or does not exist.
        )r   r   r   r7   r7   r8   partitions_for_brokerw  s   
z%ClusterMetadata.partitions_for_brokerc                 C   s   | j ||fS )aX  Return node_id of group coordinator from cache.

        Arguments:
            key (str): name of consumer group or transaction_id
            key_type (CoordinatorType, optional): Default GROUP

        Returns:
            node_id (int or str) for coordinator, -1 if coordinator unknown
            None if the group does not exist.
        )r   r   )r4   r6   key_typer7   r7   r8   get_coordinator  s   zClusterMetadata.get_coordinatorc                 C   sz   t  d }| jdur| jdr| jdd S | jrd}n|| j }| jd | }|| j }| jd | }t||dS )z/Milliseconds until metadata should be refreshedrJ   Nr\   r   r   r   )	time	monotonicr   connection_delayr   r   r0   r   max)r4   nowrP   metadata_age	retry_age
next_retryr7   r7   r8   rP     s   

zClusterMetadata.ttlc                 C   s
   | j d S )zDReturn milliseconds to wait before attempting to retry after failurer   )r0   r;   r7   r7   r8   refresh_backoff  s   
zClusterMetadata.refresh_backoffc                 C   sv   | j . d| _| jr| jjrt | _| j}| jr)|   | j  W d   |S W d   |S 1 s4w   Y  |S )a   Flags metadata for update, return Future()

        Actual update must be handled separately. This method will only
        change the reported ttl()

        Returns:
            kafka.future.Future (value will be the cluster object after update)
        TN)	r$   r   r    r:   r   r   rH   r,   rB   )r4   retr7   r7   r8   rw     s   	

zClusterMetadata.request_updatec                 C   s   | j S rt   )r   r;   r7   r7   r8   need_update  s   zClusterMetadata.need_updatec                 C   s    t | j }|r|| j S |S )a  Get set of known topics.

        Arguments:
            exclude_internal_topics (bool): Whether records from internal topics
                (such as offsets) should be exposed to the consumer. If set to
                True the only way to receive records from an internal topic is
                subscribing to it. Default True

        Returns:
            set: {topic (str), ...}
        )r   r   r   r'   )r4   exclude_internal_topicsrx   r7   r7   r8   rx     s   
zClusterMetadata.topicsc                 C   sB   | j rtj}n| jstj}ndd | jD }t|| jd dddS )Nc                 S   s   g | ]}t j|d qS ))name)r   MetadataRequestTopic)r   ry   r7   r7   r8   r     s    z4ClusterMetadata.metadata_request.<locals>.<listcomp>r   F)rx   allow_auto_topic_creation%include_cluster_authorized_operations#include_topic_authorized_operations)r%   r   
ALL_TOPICSr   	NO_TOPICSr0   )r4   rx   r7   r7   r8   rc     s   z ClusterMetadata.metadata_requestc                 C   r   )zReturn the topic UUID for ``topic_name``, or None if unknown.

        Populated from MetadataResponse v10+ (Kafka 2.8+, KIP-516). Older
        responses leave this empty.
        )r   r   )r4   
topic_namer7   r7   r8   topic_id     zClusterMetadata.topic_idc                 C   r   )zReturn the topic name for ``topic_id`` (uuid.UUID), or None.

        Reverse lookup of :meth:`topic_id`. Populated from MetadataResponse
        v10+ (KIP-516).
        )r   r   )r4   r   r7   r7   r8   topic_name_for_id  r   z!ClusterMetadata.topic_name_for_idc                 C   s`   d}| j  | jr| j}d| _W d   n1 sw   Y  t d | _|r.|| dS dS )z4Update cluster state given a failed MetadataRequest.NrJ   )r$   r    r   r   r   rY   )r4   	exceptionfr7   r7   r8   re     s   zClusterMetadata.failed_updatec              
   C   s  |j std | t|S i }|j D ]"}|jdkr$|\}}}d}n|\}}}}||t	||||i q|jdkr@d}n|
|j}|jdk rNd}	n|j}	i }
tt}t }t }i }i }t }|jdk}|jD ]}|j}|jrz|| t|j}|j}d}|dur|dur| j
|}|dur||krtd||| d}|||< |||< |tju ri |
|< |jD ]"}|j}|r|rd	|_||
| |< |jd	kr||j t|| qqm|| jv r9t|tj r|| |tj!u rtd
| qm|tj"u rt#d| qm|tj$u rt#d| || qm|tj%u r2t#d| || jv r1| j&| qmt#d|| qm| j'< || _(|| _)|	| _|
| _*|| _+|| _,|| _-|jdkr_|| _|| _.t/|dk| _0d}| j1ro| j1}d| _1W d   n	1 s}w   Y  t23 d }|| _4|| _5|rt/|jdkr|jd d tjj6kr|jd dd \}}t||}|7| n|8|  t9d|  | j:D ]}||  qdS )zUpdate cluster state given a MetadataResponse.

        Arguments:
            metadata (MetadataResponse): broker response to a metadata request

        Returns: None
        z9No broker metadata found in MetadataResponse -- ignoring.r   N   	   Fz\Topic %s topic_id changed from %s to %s -- likely recreated; resetting cached leader epochs.Tr   z;Topic %s is not available during auto-create initializationz&Topic %s not found in cluster metadataz*Topic %s is not authorized for this clientz'%s' is not a valid topic namez(Error fetching metadata for topic %s: %s
   rJ      zUpdated metadata: %s);ro   rL   r_   re   rR   MetadataEmptyBrokerListAPI_VERSIONupdater   rm   r   controller_idr)   r   r   r   rx   r   is_internalr{   for_code
error_coder   r   NoError
partitionspartition_indexr   r   r   r   
issubclassRetriableErrorLeaderNotAvailableErrorUnknownTopicOrPartitionErrorrU   TopicAuthorizationFailedErrorInvalidTopicErrorremover$   r   r(   r   r   r&   r'   r   lenr   r    r   r   r   r   errnorY   rZ   rO   r!   )r4   r\   _new_brokersr   r[   rq   rr   rack_new_controller_new_cluster_id_new_partitions_new_broker_partitions_new_unauthorized_topics_new_internal_topics_new_topic_ids_new_topic_names_by_id_retry_topicsepoch_reliabletry   
error_typenew_topic_id	recreatedpriorr   r   r   r   r   rU   listenerr7   r7   r8   rf     s   

















(

zClusterMetadata.update_metadatac                 C   s   | j | dS )z<Add a callback function to be called on each metadata updateN)r!   r{   r4   r   r7   r7   r8   add_listener  s   zClusterMetadata.add_listenerc                 C   s(   z	| j | W dS  ty   Y dS w )z,Remove a previously added listener callback.N)r!   r   KeyErrorr   r7   r7   r8   remove_listener  s
   zClusterMetadata.remove_listenerc                 C   s   t |}td|j|| t|j}|tjur'|d|j|t	|ddf |r0d
|j}n|j}t||j|jd}td|j|| || j|< || j||f< |S )ac  Update with metadata for a group or txn coordinator

        Arguments:
            response (FindCoordinatorResponse): broker response
            key_type (CoordinatorType): GROUP / TRANSACTION / SHARE
            key (str): consumer_group or transactional_id
            synthesize_node_id (bool): If True synthesizes a unique
                node_id to generate a dedicated network connection for
                coordinator requests. Default: True.

        Returns:
            string: coordinator node_id.

        Raises:
            BrokerResponseError: if ``response.error_code`` is non-zero.
        z"Updating coordinator for %s/%s: %sz+FindCoordinatorResponse error for %s/%s: %serror_message zcoordinator-{}NzCoordinator for %s/%s is %s)r   
build_fromrL   rM   r   rR   r   r   r   getattrformatr[   r   rm   rq   rr   rO   r3   r   )r4   rh   r   r6   synthesize_node_idr   r[   coordinatorr7   r7   r8   add_coordinator  s,   


zClusterMetadata.add_coordinatorc                 C   s    dt | jt | jt | jf S )Nz:ClusterMetadata(brokers: %d, topics: %d, coordinators: %d))r   r   r   r   r;   r7   r7   r8   __str__  s   zClusterMetadata.__str__rt   T)-__name__
__module____qualname____doc__r/   r9   propertyr<   rA   rC   rH   rG   rQ   rX   r1   ru   rz   r}   ro   r   r   r   r   r   r   r   r   r   r   GROUPr   rP   r   rw   r   rx   rc   r   r   re   rf   r   r   r   r   r7   r7   r7   r8   r
      s`    "


 

 
*r
   Tc                 C   sd   t | tr|  d} g }| D ]}tdd|}t|\}}}||||f q|r0t	| |S )z
    Processes a list (or comma-separated string) of hosts strings (host:port)
    and returns a list of (host, port, family) tuples.
    Optionally randomizes the returned list.
    ,z^.*://r   )

isinstancestrstripsplitresubget_ip_port_afiappendrandomshuffle)hosts	randomizeresult	host_portrq   rr   afir7   r7   r8   rj     s   

rj   c                 C   s   g }| D ]Z\}}}zt |||t jdt j}W n# t jy: } ztd||| ||||f W Y d}~qd}~ww t }|D ]\}}	}
}}|pJ|}||v rPq@|	| ||||f q@q|S )a0  Expand each bootstrap entry to one entry per canonical FQDN.

    Mirrors Java's ``client.dns.lookup=resolve_canonical_bootstrap_servers_only``:
    forward-resolve each host, take the ``canonname`` reported by the resolver,
    and emit one bootstrap entry per unique canonical name. Useful for
    Kerberos round-robin DNS deployments where the principal must match each
    individual broker FQDN.

    If a host fails to resolve, the original entry is preserved verbatim --
    matching Java's best-effort behaviour so bootstrap doesn't fail outright.
    r   zKCanonical bootstrap resolution failed for %s:%s: %s; keeping original entryN)
socketgetaddrinfoSOCK_STREAMAI_CANONNAMEgaierrorrL   r_   r   r   r{   )r  expandedrq   rr   r  	addrinfosrW   seenfamily	_socktype_proto	canonname	_sockaddrr   r7   r7   r8   rk     s.   
rk   c                 C   sb   |  dr| drtjS tjtjfD ]}zt||  |W   S  tttjfy-   Y qw tj	S )z
        Attempt to determine the family of an address (or hostname)

        :return: either socket.AF_INET or socket.AF_INET6 or socket.AF_UNSPEC if the address family
                 could not be determined
    [])

startswithendswithr  AF_INET6AF_INET	inet_pton
ValueErrorAttributeErrorrU   	AF_UNSPEC)addressafr7   r7   r8   _address_family  s   
r   i#  c              	   C   s   |   } | dr)tj}| dd d\}}|r"t|dd }nt}|||fS d| vr6t| }| t|fS zttj|  | ttjfW S  t	yR   t
d Y n ttjfy]   Y nw | dd\}}t|}t|}|||fS )au  
        Parse the IP and port from a string in the format of:

            * host_or_ip          <- Can be either IPv4 address literal or hostname/fqdn
            * host_or_ipv4:port   <- Can be either IPv4 address literal or hostname/fqdn
            * [host_or_ip]        <- IPv6 address literal
            * [host_or_ip]:port.  <- IPv6 address literal

        .. note:: IPv6 address literals with ports *must* be enclosed in brackets

        .. note:: If the port is not specified, default will be returned.

        :return: tuple (host, port, afi), afi will be socket.AF_INET or socket.AF_INET6 or socket.AF_UNSPEC
    r  r   Nr  :zUsocket.inet_pton not available on this platform. consider `pip install win_inet_pton`)r   r  r  r  r   intDEFAULT_KAFKA_PORTr   r  r  rL   r_   r  rU   rsplit)host_and_port_strr  rq   restrr   r7   r7   r8   r     s0   




r   r   )"r   r.   loggingr   r   r  r"   r   uuidr=   kafkar   rR   kafka.futurer   kafka.net.wakeup_notifierr   kafka.protocol.metadatar   r   r   kafka.structsr   
kafka.utilr	   	getLoggerr   rL   r
   rj   rk   r   r#  r   r7   r7   r7   r8   <module>   s8    
     
, 