
­­­­­­­­­­­­­­­­­­
<!DOCTYPE html>
<html>
3
\me9T                 @   sl  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mZmZ d dlZd dlmZmZmZ d dlmZmZmZmZmZ ejeZd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"G dd deZ#dd Z$d$ddZ%dd Z&d%ddZ'd&ddZ(G dd de j)Z*G dd de*Z+G d d! d!Z,G d"d# d#e*Z-dS )'    N)StringIO)AnyDictList)subp
temp_utilsutil)find_fallback_nicget_devicelistget_ib_interface_hwaddrget_interface_macis_ib_interfacez/run/systemd/netif/leasesaN  #!/bin/sh
log() {
    echo "udhcpc[$PPID]" "$interface: $2"
}
[ -z "$1" ] && echo "Error: should be called from udhcpc" && exit 1
case $1 in
    bound|renew)
    cat <<JSON > "$LEASE_FILE"
{
    "interface": "$interface",
    "fixed-address": "$ip",
    "subnet-mask": "$subnet",
    "routers": "${router%% *}",
    "static_routes" : "${staticroutes}"
}
JSON
    ;;
    deconfig)
    log err "Not supported"
    exit 1
    ;;
    leasefail | nak)
    log err "configuration failed: $1: $message"
    exit 1
    ;;
    *)
    echo "$0: Unknown udhcpc command: $1" >&2
    exit 1
    ;;
esac
c               @   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   /usr/lib/python3.6/dhcp.pyr   A   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.
    N)r   r   r   r   r   r   r   r   r   E   s   r   c               @   s   e Zd ZdZdS )NoDHCPLeaseInterfaceErrorz7Raised when unable to find a viable interface for DHCP.N)r   r   r   r   r   r   r   r   r   M   s   r   c               @   s   e Zd ZdZdS )NoDHCPLeaseMissingDhclientErrorz$Raised when unable to find dhclient.N)r   r   r   r   r   r   r   r   r   Q   s   r   c               @   s   e Zd ZdZdS )NoDHCPLeaseMissingUdhcpcErrorz)Raised when unable to find udhcpc client.N)r   r   r   r   r   r   r   r   r   U   s   r   c             C   sZ   xN| j D ]D}y| }tjd|j |S  ttfk
rJ   tjd|j Y qX qW t dS )zdistros set priority list, select based on this order which to use

    If the priority dhcp client isn't found, fall back to lower in list.
    zDHCP client selected: %szDHCP client not found: %sN)Zdhcp_client_priorityLOGdebugclient_namer   r   warning)distroclientZdhcp_clientr   r   r   select_dhcp_clientY   s    r   c             C   sZ   |dkr(t  }|dkrDtjd t n|t krDtjd| t t| }|j||| S )a  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.
    @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.)r	   r   r   r   r
   r   dhcp_discovery)r   Znicdhcp_log_funcr   r   r   r   maybe_perform_dhcp_discoveryk   s    

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)dict	configobjZ	ConfigObjr   )Zcontentr   r   r   networkd_parse_lease   s    r%   c             C   sT   | dkrt } i }tjj| s |S x.tj| D ] }ttjtjj| |||< q,W |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_DIRospathisdirlistdirr%   r   	load_filejoin)leases_dretZlfiler   r   r   networkd_load_leases   s    r/   c             C   sF   |d krt }t|d}x*t|j D ]\}}|j| r$||  S q$W d S )N)r-   )r&   r/   sorteditemsget)Zkeynamer-   ZleasesZ_ifindexdatar   r   r   networkd_get_option_from_leases   s    

r4   c               @   sL   e Zd ZdZedd Zedd ZeedddZeedd	d
Z	dS )
DhcpClient c             C   s   t j d| jgddgd d S )NZpkillr      )rcs)r   r   )clsr   r   r   kill_dhcp_client   s    zDhcpClient.kill_dhcp_clientc             C   s.   | j   tjd}x|D ]}tj| qW d S )Nz/var/lib/dhcp/*)r:   globr'   remove)r9   filesfiler   r   r   clear_leases   s    

zDhcpClient.clear_leases)dhcp_interfacec             C   s   |j d| j|ddgd d S )Nstartr   r7   )r8   )manage_servicer   )r9   r@   r   r   r   r   start_service   s    zDhcpClient.start_servicec             C   s   |j d| jddgd d S )Nstopr   r7   )r8   )rB   r   )r9   r@   r   r   r   r   stop_service   s    zDhcpClient.stop_serviceN)
r   r   r   r   classmethodr:   r?   strrC   rE   r   r   r   r   r5      s   r5   c               @   st   e Zd ZdZdd Zeeeeee	f  dddZ
ddd	Zed
d Zedd ZedddZedd ZdS )IscDhclientdhclientc             C   s&   t jd| _| js"tjd t d S )NrI   z7Skip dhclient configuration: No dhclient command found.)r   whichdhclient_pathr   r   r   )selfr   r   r   __init__   s
    zIscDhclient.__init__)
lease_filereturnc             C   s   t jdt j}g }tj| }t|dkr6tdj| xh|j|D ]Z}g }xB|j	dD ]4}|j
 jddjdd}|sxqV|j|j	dd	 qVW |jt| qBW |std
j| |S )a7  Parse the given dhcp lease file returning all leases as dicts.

        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};"r6   zoption  r7   z1Cannot parse dhcp lease file {0}. No leases found)recompileDOTALLr   r+   lenr   formatfindallsplitstripreplaceappendr#   )rN   Zlease_regexZdhcp_leasesZlease_contentZleaseZlease_optionsliner   r   r   parse_dhcp_lease_file   s&    

z!IscDhclient.parse_dhcp_lease_fileNc          !   C   s  t jd| d}d}d}tjt tj| tj| W dQ R X |jj| t	|rdt
|dd  }d||f }tjdd	}	tjj|	|d
 }tj|| y"tj|j| j||||\}
}W nB tjk
r } z"t jd|j|j|j t|W Y dd}~X nX tj||gddd}|r@t jddjdd |D  g S d}d}xtddD ]v}tj|j }yt|}W n tk
r   Y n6X tj |}|dkrt jd| tj!|t"j# d}P t$j%d qTW |st j&d||d |dk	r||
| | j'|S )a  Run dhclient on the interface without scripts/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.leaseNz20:%s$   z0interface "%s" {send dhcp-client-identifier %s;}T)	needs_exez-dhclient.confz3dhclient exited with code: %s stderr: %r stdout: %r   g{Gz?)ZmaxwaitZnaplenz+dhclient did not produce expected files: %sz, c             s   s   | ]}t jj|V  qd S )N)r'   r(   basename).0fr   r   r   	<genexpr>I  s    z-IscDhclient.dhcp_discovery.<locals>.<genexpr>unknownFr   i  r7   zkilling dhclient with pid=%szCdhclient(pid=%s, parentpid=%s) failed to daemonize after %s secondsg      $@)(r   r   
contextlibsuppressFileNotFoundErrorr'   r<   net_opslink_upr   r   r   get_tmp_ancestorr(   r,   r   
write_filer   Zbuild_dhclient_cmdrK   ProcessExecutionError	exit_codestderrstdoutr   Zwait_for_filesr   ranger+   rZ   int
ValueErrorZget_proc_ppidkillsignalSIGKILLtimeZsleeperrorr^   )rL   	interfacer!   r   Zpid_filerN   Zconfig_filedhcp_client_identifierZinterface_dhclient_contenttmp_dirouterrry   ZmissingZppidZ
daemonized_Zpid_contentpidr   r   r   r       sz    

	



zIscDhclient.dhcp_discoveryc                s   j d dd tjd D }g } fdd}d}xt|D ]\}}||k rTq@t|}|tdd	krd
}t||d |k r|||t||d  |S dj||d |d  }	dj||d ||  }
|| }n |tddkrjd}t||d |k r$|||t||d  |S dj||d |d  dg }	dj||d ||  }
|| }n|td
dkrd}t||d |k r|||t||d  |S dj||d |d  ddg }	dj||d ||  }
|| }n|tdd
krd}t||d |k r>|||t||d  |S dj||d |d  dddg }	dj||d ||  }
|| }nx|dkrd}t||d |k r|||t||d  |S d}	dj||d ||  }
|| }ntj	d| |S |j
d|	|f |
f q@W |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
        rP   c             S   s   g | ]}|r|qS r   r   )rc   tokr   r   r   
<listcomp>  s    z3IscDhclient.parse_static_routes.<locals>.<listcomp>z[, .]c                s   d| || f }t j| 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   ry   )ZcidrZrequiredZremainmsg)rfc3442r   r   _trunc_error  s    z5IscDhclient.parse_static_routes.<locals>._trunc_errorr      !   	   N.r7   ra            0            z0.0.0.0zSParsed invalid net length "%s".  Verify DHCP rfc3442-classless-static-routes value.z%s/%s)rstriprS   rY   	enumeraters   rr   rV   r,   r   ry   r\   )r   tokensstatic_routesr   Zcurrent_idxidxr   Z
net_lengthZreq_toksZnet_addressZgatewayr   )r   r   parse_static_routesj  sr    !
	 " 


zIscDhclient.parse_static_routesc              C   sJ   dddg} x:| D ]2}t jj|rtt j|dkrtjd| |S qW d S )Nz/var/lib/dhclientz/var/lib/dhcpz/var/lib/NetworkManagerr   zUsing %s lease directory)r'   r(   existsrV   r*   r   r   )Zsupported_dirsdr   r   r   get_dhclient_d  s    
zIscDhclient.get_dhclient_dc             C   s~   | d krt j } | sd S tj| }d}d }xN|D ]F}|jdr@q0|jdsLq0tjj| |}tjj|}||kr0|}|}q0W |S )Nr7   Z	dhclient6.lease.leases)r   r   )	rH   r   r'   r*   
startswithendswithr(   r,   getmtime)Zlease_dZlease_filesZlatest_mtimeZlatest_fileZfnameZabs_pathmtimer   r   r   get_latest_lease  s$    




zIscDhclient.get_latest_leasec             C   sd   t | dP}xH|D ]@}d|kr|jdjd}t|dkr|d }tjd| |}qW W d Q R X |S )Nrzdhcp-server-identifierz ;
rR   r   zFound DHCP identifier %s)openrZ   rY   rV   r   r   )rN   fdr]   ZwordsZdhcptokZlatest_addressr   r   r   !parse_dhcp_server_from_lease_file  s    
z-IscDhclient.parse_dhcp_server_from_lease_file)NN)N)r   r   r   r   rM   staticmethodrG   r   r   r   r^   r    r   r   r   r   r   r   r   r   rH      s   $ 
rk rH   c               @   s   e Zd ZdZdd ZdS )DhcpcdZdhcpcdc             C   s   t dd S )NzDhcpcd not yet implemented)r   )rL   r   r   r   rM     s    zDhcpcd.__init__N)r   r   r   r   rM   r   r   r   r   r     s   r   c               @   s"   e Zd ZdZdd ZdddZdS )Udhcpcudhcpcc             C   s&   t jd| _| js"tjd t d S )Nr   z3Skip udhcpc configuration: No udhcpc command found.)r   rJ   udhcpc_pathr   r   r   )rL   r   r   r   rM     s    
zUdhcpc.__init__Nc             C   s  t jd| tjdd}tjj||d }tjt	 tj
| W dQ R X |jj| tjj|d}tj|td | jdd	d
|d|ddddg}t|rt|dd}|jdd|jdd g ytj|d|idd\}	}
W nB tjk
r } z"t jd|j|j|j t|W Y dd}~X nX |dk	r0||	|
 tjtj|}|d j }|r~dd t|ddd |ddd D |d< |gS )as  Run udhcpc on the interface without scripts or filesystem artifacts.

        @param interface: Name of the network interface on which to run udhcpc.
        @param dhcp_log_func: A callable accepting the udhcpc output and
            error streams.

        @return: A list of dicts of representing the dhcp leases parsed from
            the udhcpc lease file.
        z!Performing a dhcp discovery on %sT)r`   z.lease.jsonNudhcpc_scripti  z-OZstaticroutesz-iz-sz-nz-qz-fz-v)Zethernet_formatz-xz0x3d:%s:r6   Z
LEASE_FILE)Z
update_envZcapturez1udhcpc exited with code: %s stderr: %r stdout: %rr   c             S   s   g | ]}|qS r   r   )rc   ir   r   r   r   k  s    z)Udhcpc.dhcp_discovery.<locals>.<listcomp>r   r7   )r   r   r   rl   r'   r(   r,   rg   rh   ri   r<   rj   rk   r   rm   UDHCPC_SCRIPTr   r   r   extendr[   r   rn   ro   rp   rq   r   Z	load_jsonr+   rY   zip)rL   rz   r!   r   r|   rN   r   cmdr{   r}   r~   ry   Z
lease_jsonr   r   r   r   r    !  sR    


,zUdhcpc.dhcp_discovery)NN)r   r   r   r   rM   r    r   r   r   r   r     s   	 r   )NN)N)N).abcrg   r;   Zloggingr'   rS   rv   rx   ior   Ztypingr   r   r   r$   Z	cloudinitr   r   r   Zcloudinit.netr	   r
   r   r   r   Z	getLoggerr   r   r&   r   	Exceptionr   r   r   r   r   r   r"   r%   r/   r4   ABCr5   rH   r   r   r   r   r   r   <module>   s>   




  L