o
    w7e9T                     @   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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                   @      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   A       r   c                   @   r   )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   E   r   r   c                   @   r   )NoDHCPLeaseInterfaceErrorz7Raised when unable to find a viable interface for DHCP.Nr   r   r   r   r   r   M   r   r   c                   @   r   )NoDHCPLeaseMissingDhclientErrorz$Raised when unable to find dhclient.Nr   r   r   r   r   r   Q   r   r   c                   @   r   )NoDHCPLeaseMissingUdhcpcErrorz)Raised when unable to find udhcpc client.Nr   r   r   r   r   r   U   r   r   c              
   C   sT   | j D ]#}z| }td|j |W   S  ttfy&   td|j Y qw t )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: %s)dhcp_client_priorityLOGdebugclient_namer   r   warning)distroclientdhcp_clientr   r   r   select_dhcp_clientY   s   


r$   c                 C   sZ   |du rt  }|du rtd t n|t vr"td| t t| }|||| 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!   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)list_values)dict	configobj	ConfigObjr   )contentr   r   r   networkd_parse_lease   s   r.   c                 C   sP   | du 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_DIRospathisdirlistdirr.   r   	load_filejoin)leases_dretlfiler   r   r   networkd_load_leases   s   
r9   c                 C   sF   |d u rt }t|d}t| D ]\}}|| r ||    S qd S )N)r6   )r/   r9   sorteditemsget)keynamer6   leases_ifindexdatar   r   r   networkd_get_option_from_leases   s   

rA   c                   @   sL   e Zd ZdZedd Zedd ZedefddZedefd	d
Z	dS )
DhcpClient c                 C   s   t j d| jgddgd d S )Npkillr      rcs)r   r   )clsr   r   r   kill_dhcp_client   s   zDhcpClient.kill_dhcp_clientc                 C   s*   |    td}|D ]}t| qd S )Nz/var/lib/dhcp/*)rI   globr0   remove)rH   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   rE   rF   manage_servicer   rH   rO   r!   r   r   r   start_service   s   
zDhcpClient.start_servicec                 C   s   |j d| jddgd d S )Nstopr   rE   rF   rQ   rS   r   r   r   stop_service   s   zDhcpClient.stop_serviceN)
r   r   r   r   classmethodrI   rN   strrT   rV   r   r   r   r   rB      s    

rB   c                   @   sz   e Zd ZdZdd Zededeeee	f  f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   &   t d| _| jstd t d S )NrZ   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 dt j}g }t| }t|dkrtd| ||D ]+}g }|	dD ]}|
 dddd}|s:q)||	dd	 q)|t| q |sUtd
| |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};"rC   zoption  rE   z1Cannot parse dhcp lease file {0}. No leases found)recompileDOTALLr   r4   lenr   formatfindallsplitstripreplaceappendr*   )ra   lease_regexdhcp_leaseslease_contentleaselease_optionsliner   r   r   parse_dhcp_lease_file   s,   

z!IscDhclient.parse_dhcp_lease_fileNc              
   C   s  t d| d}d}d}tt t| t| W d   n1 s&w   Y  |j| t	|rZdt
|dd  }d||f }tjdd	}	tj|	|d
 }t|| zt|| j||||\}
}W n tjy } zt d|j|j|j t|d}~ww tj||gddd}|rt dddd |D  g S d}d}tddD ]8}t| }zt|}W n	 ty   Y nw t |}|dkrt d| t!|t"j# d} nt$%d q|st &d||d |dur||
| | '|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.
        !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?)maxwaitnaplenz+dhclient did not produce expected files: %sz, c                 s   s    | ]	}t j|V  qd S N)r0   r1   basename).0fr   r   r   	<genexpr>I  s    z-IscDhclient.dhcp_discovery.<locals>.<genexpr>unknownFr   i  rE   zkilling dhclient with pid=%szCdhclient(pid=%s, parentpid=%s) failed to daemonize after %s secondsg      $@)(r   r   
contextlibsuppressFileNotFoundErrorr0   rK   net_opslink_upr   r   r   get_tmp_ancestorr1   r5   r   
write_filer   build_dhclient_cmdr]   ProcessExecutionError	exit_codestderrstdoutr   wait_for_filesr    ranger4   rm   int
ValueErrorget_proc_ppidkillsignalSIGKILLtimesleeperrorrv   )r_   	interfacer'   r!   pid_filera   config_filedhcp_client_identifierinterface_dhclient_contenttmp_dirouterrr   missingppid
daemonized_pid_contentpidr   r   r   r%      s   
	



zIscDhclient.dhcp_discoveryc                    s     d dd td D }g } fdd}d}t|D ]n\}}||k r(qt|}|tdd	v rod
}t||d |k rO|||t||d  |  S d||d |d  }	d||d ||  }
|| }n|tddv rd}t||d |k r|||t||d  |  S d||d |d  dg }	d||d ||  }
|| }n|td
dv rd}t||d |k r|||t||d  |  S d||d |d  ddg }	d||d ||  }
|| }n|tdd
v rBd}t||d |k r|||t||d  |  S d||d |d  g d }	d||d ||  }
|| }n@|dkrxd}t||d |k rd|||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
        rc   c                 S   s   g | ]}|r|qS r   r   )r   tokr   r   r   
<listcomp>  s    z3IscDhclient.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   r   )cidrrequiredremainmsgrfc3442r   r   _trunc_error  s
   
z5IscDhclient.parse_static_routes.<locals>._trunc_errorr      !   	   N.rE   r{            0            )r   r   r   z0.0.0.0zSParsed invalid net length "%s".  Verify DHCP rfc3442-classless-static-routes value.z%s/%s)rstriprf   rl   	enumerater   r   ri   r5   r   r   ro   )r   tokensstatic_routesr   current_idxidxr   
net_lengthreq_toksnet_addressgatewayr   r   r   parse_static_routesj  sx   
!	 
"



zIscDhclient.parse_static_routesc                  C   sH   g d} | D ]}t j|r!tt |dkr!td| |  S qd S )N)z/var/lib/dhclientz/var/lib/dhcpz/var/lib/NetworkManagerr   zUsing %s lease directory)r0   r1   existsri   r3   r   r   )supported_dirsdr   r   r   get_dhclient_d  s   zIscDhclient.get_dhclient_dc                 C   sz   | d u rt  } | sd S t| }d}d }|D ]#}|drq|ds%qtj| |}tj|}||kr:|}|}q|S )N	dhclient6)z.leasez.leases)	rY   r   r0   r3   
startswithendswithr1   r5   getmtime)lease_dlease_fileslatest_mtimelatest_filefnameabs_pathmtimer   r   r   get_latest_lease  s&   



zIscDhclient.get_latest_leasec                 C   sv   t | d,}|D ] }d|v r(|dd}t|dkr(|d }td| |}qW d    |S 1 s4w   Y  |S )Nrzdhcp-server-identifierz ;
re   r   zFound DHCP identifier %s)openrm   rl   ri   r   r   )ra   fdru   wordsdhcptoklatest_addressr   r   r   !parse_dhcp_server_from_lease_file  s   
z-IscDhclient.parse_dhcp_server_from_lease_fileNNr~   )r   r   r   r   r`   staticmethodrX   r   r   r   rv   r%   r   r   r   r   r   r   r   r   rY      s      $
v
j
 rY   c                   @   s   e Zd ZdZdd ZdS )Dhcpcddhcpcdc                 C   s   t d)NzDhcpcd not yet implemented)r   r^   r   r   r   r`     s   zDhcpcd.__init__N)r   r   r   r   r`   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   r[   )Nr   z3Skip udhcpc configuration: No udhcpc command found.)r   r\   udhcpc_pathr   r   r   r^   r   r   r   r`     s
   
zUdhcpc.__init__Nc                 C   s  t d| tjdd}tj||d }tt	 t
| W d   n1 s*w   Y  |j| tj|d}t|td | jdd	d
|d|ddddg}t|rht|dd}|dd|dd g ztj|d|idd\}	}
W n tjy } zt d|j|j|j t|d}~ww |dur||	|
 tt|}|d  }|rd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.
        rw   Try   z.lease.jsonNudhcpc_scripti  z-Ostaticroutesz-iz-sz-nz-qz-fz-v)ethernet_formatz-xz0x3d:%s:rC   
LEASE_FILE)
update_envcapturez1udhcpc exited with code: %s stderr: %r stdout: %rr   c                 S   s   g | ]}|qS r   r   )r   ir   r   r   r   j  s    z)Udhcpc.dhcp_discovery.<locals>.<listcomp>r   rE   )r   r   r   r   r0   r1   r5   r   r   r   rK   r   r   r   r   UDHCPC_SCRIPTr   r   r   extendrn   r   r   r   r   r   r   	load_jsonr4   rl   zip)r_   r   r'   r!   r   ra   r   cmdr   r   r   r   
lease_jsonr   r   r   r   r%   !  sf   
	

zUdhcpc.dhcp_discoveryr   )r   r   r   r   r`   r%   r   r   r   r   r     s    	r   r   r~   ).abcr   rJ   loggingr0   rf   r   r   ior   typingr   r   r   r+   	cloudinitr   r   r   cloudinit.netr	   r
   r   r   r   	getLoggerr   r   r/   r   	Exceptionr   r   r   r   r   r$   r(   r.   r9   rA   ABCrB   rY   r   r   r   r   r   r   <module>   s@   
!



  L