
"T`0                 @   s  d  d l  m Z d  d l  m Z d  d l m Z d  d l Z d  d l Z d  d l Z d  d l	 Z	 e j
 e  Z d Z d Z d Z d Z d Z d	 Z d
 Z d Z d Z d Z d Z d Z d Z d Z e	 j e  Z e	 j e  Z e e Z d Z d Z  d Z! d Z" d  Z# d Z$ d Z% d Z& d Z' d Z( d Z) e d d d d g  Z* e d d d g  Z+ e d d d d d d g  Z, Gd  d!   d! e-  Z. d" d#   Z/ d$ d%   Z0 d d& d'  Z1 d( d)   Z2 d* d+   Z3 d, d-   Z4 d. d/   Z5 d0 d1   Z6 d2 d3   Z7 d S)4    )log)util)
namedtupleN                     i      <   ZIHHIIZBHiII         RTAAttrlengthrta_typedataInterfaceOperstateifname	operstateNetlinkHeadertypeflagsseqpidc               @   s   e  Z d  Z d Z d S)NetlinkCreateSocketErrorz5Raised if netlink socket fails during create or bind.N)__name__
__module____qualname____doc__ r"   r"   C/usr/lib/python3/dist-packages/cloudinit/sources/helpers/netlink.pyr   8   s   r   c              C   s   yH t  j  t  j t  j t  j  }  |  j t j   t f  |  j d  WnA t  j	 k
 r } z d | } t
 |  |  WYd d } ~ Xn Xt j d  |  S)au  Creates netlink socket and bind on netlink group to catch interface
    down/up events. The socket will bound only on RTMGRP_LINK (which only
    includes RTM_NEWLINK/RTM_DELLINK/RTM_GETLINK events). The socket is set to
    non-blocking mode since we're only receiving messages.

    :returns: netlink socket in non-blocking mode
    :raises: NetlinkCreateSocketError
    r   z*Exception during netlink socket create: %sNzCreated netlink socket)socketZ
AF_NETLINKZSOCK_RAWZNETLINK_ROUTEZbindosgetpidRTMGRP_LINKZsetblockingerrorr   LOGdebug)netlink_socketemsgr"   r"   r#   create_bound_netlink_socket<   s    	
"r.   c             C   s   |  d k	 s t  d   t |   t k s6 t  d   t j t |  d t   \ } } } } } t j d |  t	 | | | | |  S)a  Gets netlink message type and length

    :param: data read from netlink socket
    :returns: netlink message type
    :raises: AssertionError if data is None or data is not >= NLMSGHDR_SIZE
    struct nlmsghdr {
               __u32 nlmsg_len;    /* Length of message including header */
               __u16 nlmsg_type;   /* Type of message content */
               __u16 nlmsg_flags;  /* Additional flags */
               __u32 nlmsg_seq;    /* Sequence number */
               __u32 nlmsg_pid;    /* Sender port ID */
    };
    Nzdata is nonez+data is smaller than netlink message headerzGot netlink msg of type %d)
AssertionErrorlenNLMSGHDR_SIZEstructZunpackNLMSGHDR_FMTMSG_TYPE_OFFSETr)   r*   r   )r   Zmsg_lenZmsg_typer   r   r   r"   r"   r#   get_netlink_msg_headerR   s    		"r5   c             C   s   |  d k	 s t  d   t j |  g g  g  |  \ } } } |  | k rL d St j d  |  j t  } | d k r t j d  | S)a  Select and read from the netlink socket if ready.

    :param: netlink_socket: specify which socket object to read from
    :param: timeout: specify a timeout value (integer) to wait while reading,
            if none, it will block indefinitely until socket ready for read
    :returns: string of data read (max length = <MAX_SIZE>) from socket,
              if no data read, returns None
    :raises: AssertionError if netlink_socket is None
    Nznetlink socket is noneznetlink socket ready for readz,Reading from Netlink socket returned no data)r/   selectr)   r*   ZrecvMAX_SIZEr(   )r+   ZtimeoutZread_set_r   r"   r"   r#   read_netlink_socketi   s    
$r9   c             C   s   |  d k	 s t  d   t |  t k s6 t  d   | t k sN t  d   d } } d } y@ t j d |  d | d } t j d |  d | d d } Wn t j k
 r d SYn X|  | t | |  } t | | |  S)	a(  Unpack a single rta attribute.

    :param: data: string of data read from netlink socket
    :param: offset: starting offset of RTA Attribute
    :return: RTAAttr object with length, type and data. On error, return None.
    :raises: AssertionError if data is None or offset is not integer.
    Nzdata is nonezoffset is not integerz'rta offset is less than expected lengthr   Hoffsetr   )	r/   r   intRTATTR_START_OFFSETr2   Zunpack_fromr(   RTA_DATA_START_OFFSETr   )r   r;   r   r   Z	attr_datar"   r"   r#   unpack_rta_attr   s    	
$	r?   c             C   s6  |  d k	 s t  d   t |   t k s6 t  d   d } } t } x | t |   k r t |  |  } | s | j d k r Pt | j t t } | | j | 7} | j t k r t | j	  } qI | j t
 k rI t j | j	 d  } | j d  } qI W| s| d k rd St j d | |  t | |  S)a  Reads Interface name and operational state from RTA Data.

    :param: data: string of data read from netlink socket
    :returns: InterfaceOperstate object containing if_name and oper_state.
              None if data does not contain valid IFLA_OPERSTATE and
              IFLA_IFNAME messages.
    :raises: AssertionError if data is None or length of data is
             smaller than RTATTR_START_OFFSET.
    Nzdata is nonez2length of data is smaller than RTATTR_START_OFFSETr   zutf-8 z!rta attrs: ifname %s operstate %d)r/   r0   r=   r?   r   PAD_ALIGNMENTr   IFLA_OPERSTATEordr   IFLA_IFNAMEr   Zdecode_binarystripr)   r*   r   )r   r   r   r;   attrpadlenZinterface_namer"   r"   r#   read_rta_oper_state   s*    
	
rH   c                sK   t  j d  d     f d d   } t |  d t g t t g |   S)zBlock until a single nic is attached.

    :param: netlink_socket: netlink_socket to receive events
    :param: existing_nics: List of existing nics so that we can skip them.
    :raises: AssertionError if netlink_socket is none.
    z!Preparing to wait for nic attach.Nc                s   |    k r d S|   d S)NTFr"   )inamecarrierprevCarrier)existing_nicsr   r"   r#   should_continue_cb   s    z5wait_for_nic_attach_event.<locals>.should_continue_cb)r)   r*   read_netlink_messagesRTM_NEWLINKOPER_UP	OPER_DOWN)r+   rL   rM   r"   )rL   r   r#   wait_for_nic_attach_event   s    		rR   c                sE   t  j d  d     f d d   } t |  d t g t g |    S)zBlock until a single nic is detached and its operational state is down.

    :param: netlink_socket: netlink_socket to receive events.
    z!Preparing to wait for nic detach.Nc                s
   |    d S)NFr"   )rI   rJ   rK   )r   r"   r#   rM      s    z5wait_for_nic_detach_event.<locals>.should_continue_cb)r)   r*   rN   RTM_DELLINKrQ   )r+   rM   r"   )r   r#   wait_for_nic_detach_event   s    rT   c                s   |  d k	 s t  d     d k	 s0 t  d   t    d k sN t  d     f d d   } t j d  t |    t t g t t g |  d S)	a  Block until media disconnect and connect has happened on an interface.
    Listens on netlink socket to receive netlink events and when the carrier
    changes from 0 to 1, it considers event has happened and
    return from this function

    :param: netlink_socket: netlink_socket to receive events
    :param: ifname: Interface name to lookout for netlink events
    :raises: AssertionError if netlink_socket is None or ifname is None.
    Nznetlink socket is nonezinterface name is noner   zinterface name cannot be emptyc                s6   | t  k o | t k } | r2 t j d    d Sd S)NzMedia switch happened on %s.FT)rQ   rP   r)   r*   )rI   rJ   rK   ZisVnetSwitch)r   r"   r#   rM      s
    z=wait_for_media_disconnect_connect.<locals>.should_continue_cbz1Wait for media disconnect and reconnect to happen)	r/   r0   r)   r*   rN   rO   rS   rP   rQ   )r+   r   rM   r"   )r   r#   !wait_for_media_disconnect_connect   s    
		rU   c             C   s  |  d k r t  d   t   } t } t } xt |  t  } | d k rN q0 t j d t |   | | 7} t j d t |   d }	 t |  }
 xN|	 |
 k  r| |	 d  } t |  t k  r t j d  Pt	 |  } t |  | j
 k  rt j d  P| j
 t d t d @} |	 | }	 t j d	 |	  | j | k rJq t |  } | d k rut j d
 |  q | d k	 r| j | k rt j d | j |  q | j | k rq | } | j } | | j | |  s d Sq W| |	 d  } q0 Wd S)a   Reads from the netlink socket until the condition specified by
    the continuation callback is met.

    :param: netlink_socket: netlink_socket to receive events.
    :param: ifname_filter: if not None, will only listen for this interface.
    :param: rtm_types: Type of netlink events to listen for.
    :param: operstates: Operational states to listen.
    :param: should_continue_callback: Specifies when to stop listening.
    NzNetlink socket is nonezread %d bytes from socketzLength of data after concat %dr   z#Data is smaller than netlink headerz*Partial data. Smaller than netlink messager   z"offset to next netlink message: %dz!Failed to read rta attributes: %sz6Ignored netlink event on interface %s. Waiting for %s.)RuntimeErrorbytesrP   r9   SELECT_TIMEOUTr)   r*   r0   r1   r5   r   rA   r   rH   r   r   )r+   Zifname_filterZ	rtm_typesZ
operstatesZshould_continue_callbackr   rJ   rK   Z	recv_datar;   ZdatalenZnl_msgZnlheaderrG   Zinterface_stater"   r"   r#   rN   
  s^    	

			rN   )8Z	cloudinitr   Zloggingr   collectionsr   r%   r6   r$   r2   Z	getLoggerr   r)   r'   Z
NLMSG_NOOPZNLMSG_ERRORZ
NLMSG_DONErO   rS   ZRTM_GETLINKZRTM_SETLINKr7   ZRTA_DATA_OFFSETr4   rX   r3   ZIFINFOMSG_FMTZcalcsizer1   ZIFINFOMSG_SIZEr=   r>   rA   rD   rB   ZOPER_UNKNOWNZOPER_NOTPRESENTrQ   ZOPER_LOWERLAYERDOWNZOPER_TESTINGZOPER_DORMANTrP   r   r   r   rV   r   r.   r5   r9   r?   rH   rR   rT   rU   rN   r"   r"   r"   r#   <module>   sb   
#