U
    Adp.                     @   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mZ d dlZd dl	m
Z
mZ d dlmZmZ eeZdZG dd deZG dd	 d	eZG d
d deZG dd deZdddZdd ZdddZdd ZdddZdddZdd ZdS )     N)StringIO)subputil)find_fallback_nicget_devicelistz/run/systemd/netif/leasesc                   @   s   e Zd ZdZdS )NoDHCPLeaseErrorz'Raised when unable to get a DHCP lease.N__name__
__module____qualname____doc__ r   r   4/usr/lib/python3/dist-packages/cloudinit/net/dhcp.pyr      s   r   c                   @   s   e Zd ZdZdS )InvalidDHCPLeaseFileErrorzRaised when parsing an empty or invalid dhclient.lease file.

    Current uses are DataSourceAzure and DataSourceEc2 during ephemeral
    boot to scrape metadata.
    Nr   r   r   r   r   r      s   r   c                   @   s   e Zd ZdZdS )NoDHCPLeaseInterfaceErrorz7Raised when unable to find a viable interface for DHCP.Nr   r   r   r   r   r   %   s   r   c                   @   s   e Zd ZdZdS )NoDHCPLeaseMissingDhclientErrorz$Raised when unable to find dhclient.Nr   r   r   r   r   r   )   s   r   c                 C   sn   | dkr(t  } | dkrDtd t n| t krDtd|  t td}|sbtd t t|| |S )aJ  Perform dhcp discovery if nic valid and dhclient command exists.

    If the nic is invalid or undiscoverable or dhclient command is not found,
    skip dhcp_discovery and return an empty dict.

    @param nic: Name of the network interface we want to run dhclient on.
    @param dhcp_log_func: A callable accepting the dhclient output and error
        streams.
    @param tmp_dir: Tmp dir with exec permissions.
    @return: A list of dicts representing dhcp options for each lease obtained
        from the dhclient discovery if run, otherwise an empty list is
        returned.
    Nz1Skip dhcp_discovery: Unable to find fallback nic.z8Skip dhcp_discovery: nic %s not found in get_devicelist.Zdhclientz7Skip dhclient configuration: No dhclient command found.)	r   LOGdebugr   r   r   Zwhichr   dhcp_discovery)Znicdhcp_log_funcZtmp_dirZdhclient_pathr   r   r   maybe_perform_dhcp_discovery-   s     

 

r   c                 C   s   t dt j}g }t| }t|dkr6td| ||D ]V}g }|	dD ]4}|
 dddd}|stqR||	dd	 qR|t| q@|std
| |S )a  Parse the given dhcp lease file for the most recent lease.

    Return a list of dicts of dhcp options. Each dict contains key value pairs
    a specific lease in order from oldest to newest.

    @raises: InvalidDHCPLeaseFileError on empty of unparseable leasefile
        content.
    zlease {(?P<lease>.*?)}\nr   z&Cannot parse empty dhcp lease file {0};" zoption     z1Cannot parse dhcp lease file {0}. No leases found)recompileDOTALLr   	load_filelenr   formatfindallsplitstripreplaceappenddict)
lease_fileZlease_regexZdhcp_leasesZlease_contentZleaseZlease_optionsliner   r   r   parse_dhcp_lease_fileL   s,    	
r*   c              
   C   sv  t d| d}d}tt t| t| W 5 Q R X tjdddd|dgd	d
 | ddd|d||ddg
}tj|d	d
\}}tj	||gddd}|rt 
dddd |D  g S d}	d}
tddD ]v}t| }zt|}W n tk
r   Y n:X t|}	|	dkr8t d| t|tj d	}
 qDtd q|
sZt d||	d |d k	rn||| t|S )!a  Run dhclient on the interface without scripts or filesystem artifacts.

    @param dhclient_cmd_path: Full path to the dhclient used.
    @param interface: Name of the network interface on which to dhclient.
    @param dhcp_log_func: A callable accepting the dhclient output and error
        streams.

    @return: A list of dicts of representing the dhcp leases parsed from the
        dhclient.lease file or empty list.
    z!Performing a dhcp discovery on %sz/run/dhclient.pidz/run/dhclient.leaseZiplinksetZdevZupT)Zcapturez-1z-vz-lfz-pfz-sfz	/bin/true   g{Gz?)ZmaxwaitZnaplenz+dhclient did not produce expected files: %sz, c                 s   s   | ]}t j|V  qd S )N)ospathbasename).0fr   r   r   	<genexpr>   s     z!dhcp_discovery.<locals>.<genexpr>unknownFr   i  r   zkilling dhclient with pid=%szCdhclient(pid=%s, parentpid=%s) failed to daemonize after %s secondsg      $@N)r   r   
contextlibsuppressFileNotFoundErrorr.   remover   r   Zwait_for_filesZwarningjoinranger   r$   int
ValueErrorZget_proc_ppidkillsignalSIGKILLtimesleeperrorr*   )Zdhclient_cmd_pathZ	interfacer   Zpid_filer(   cmdouterrZmissingZppidZ
daemonized_Zpid_contentpidr   r   r   r   n   sn    
	  



r   c                 C   s   t tjt| ddS )zParse a systemd lease file content as in /run/systemd/netif/leases/

    Parse this (almost) ini style file even though it says:
      # This is private data. Do not parse.

    Simply return a dictionary of key/values.F)Zlist_values)r'   	configobjZ	ConfigObjr   )Zcontentr   r   r   networkd_parse_lease   s    rI   c                 C   sP   | dkrt } i }tj| s |S t| D ] }tttj| |||< q*|S )zReturn a dictionary of dictionaries representing each lease
    found in lease_d.i

    The top level key will be the filename, which is typically the ifindex.N)	NETWORKD_LEASES_DIRr.   r/   isdirlistdirrI   r   r   r9   )leases_dZretZlfiler   r   r   networkd_load_leases   s    
rN   c                 C   sF   |d krt }t|d}t| D ]\}}|| r"||    S q"d S )N)rM   )rJ   rN   sorteditemsget)ZkeynamerM   ZleasesZ_ifindexdatar   r   r   networkd_get_option_from_leases   s    

rS   c                    s.    d dd td D }g } fdd}d}t|D ]\}}||k rPq<t|}|tdd	krd
}t||d |k r|||t||d  |  S d||d |d  }	d||d ||  }
|| }n4|tddkrnd}t||d |k r(|||t||d  |  S d||d |d  dg }	d||d ||  }
|| }n|td
dkr d}t||d |k r|||t||d  |  S d||d |d  ddg }	d||d ||  }
|| }n|tdd
krd}t||d |k rJ|||t||d  |  S d||d |d  dddg }	d||d ||  }
|| }n|dkrd}t||d |k r|||t||d  |  S d}	d||d ||  }
|| }nt	d| |  S |
d|	|f |
f q<|S )a  parse rfc3442 format and return a list containing tuple of strings.

    The tuple is composed of the network_address (including net length) and
    gateway for a parsed static route.  It can parse two formats of rfc3442,
    one from dhcpcd and one from dhclient (isc).

    @param rfc3442: string in rfc3442 format (isc or dhcpd)
    @returns: list of tuple(str, str) for all valid parsed routes until the
              first parsing error.

    E.g.
    sr=parse_static_routes("32,169,254,169,254,130,56,248,255,0,130,56,240,1")
    sr=[
        ("169.254.169.254/32", "130.56.248.255"), ("0.0.0.0/0", "130.56.240.1")
    ]

    sr2 = parse_static_routes("24.191.168.128 192.168.128.1,0 192.168.128.1")
    sr2 = [
        ("191.168.128.0/24", "192.168.128.1"), ("0.0.0.0/0", "192.168.128.1")
    ]

    Python version of isc-dhclient's hooks:
       /etc/dhcp/dhclient-exit-hooks.d/rfc3442-classless-routes
    r   c                 S   s   g | ]}|r|qS r   r   )r1   tokr   r   r   
<listcomp>  s      z'parse_static_routes.<locals>.<listcomp>z[, .]c                    s   d| || f }t | d S )NzRFC3442 string malformed.  Current route has CIDR of %s and requires %s significant octets, but only %s remain. Verify DHCP rfc3442-classless-static-routes value: %s)r   rB   )ZcidrZrequiredZremainmsgrfc3442r   r   _trunc_error  s
    
z)parse_static_routes.<locals>._trunc_errorr      !   	   N.r   r-            0            z0.0.0.0zSParsed invalid net length "%s".  Verify DHCP rfc3442-classless-static-routes value.z%s/%s)rstripr   r#   	enumerater;   r:   r    r9   r   rB   r&   )rX   tokensZstatic_routesrY   Zcurrent_idxidxrT   Z
net_lengthZreq_toksZnet_addressZgatewayr   rW   r   parse_static_routes   sp    
	 "$


rj   )NNN)N)N)N)r5   Zloggingr.   r   r>   r@   ior   rH   Z	cloudinitr   r   Zcloudinit.netr   r   Z	getLoggerr	   r   rJ   	Exceptionr   r   r   r   r   r*   r   rI   rN   rS   rj   r   r   r   r   <module>   s,   

"
[


