3 \me9T@slddlZddlZddlZddlZddlZddlZddlZddlZddlm Z ddl m Z m Z m Z ddlZddlmZmZmZddlmZmZmZmZmZejeZdZdZGdd d eZGd d d eZ Gd d d eZ!GdddeZ"GdddeZ#ddZ$d$ddZ%ddZ&d%ddZ'd&ddZ(Gdddej)Z*Gddde*Z+Gd d!d!Z,Gd"d#d#e*Z-dS)'N)StringIO)AnyDictList)subp temp_utilsutil)find_fallback_nicget_devicelistget_ib_interface_hwaddrget_interface_macis_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 < "$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@seZdZdZdS)NoDHCPLeaseErrorz'Raised when unable to get a DHCP lease.N)__name__ __module__ __qualname____doc__rr/usr/lib/python3.6/dhcp.pyrAsrc@seZdZdZdS)InvalidDHCPLeaseFileErrorzRaised when parsing an empty or invalid dhclient.lease file. Current uses are DataSourceAzure and DataSourceEc2 during ephemeral boot to scrape metadata. N)rrrrrrrrrEsrc@seZdZdZdS)NoDHCPLeaseInterfaceErrorz7Raised when unable to find a viable interface for DHCP.N)rrrrrrrrrMsrc@seZdZdZdS)NoDHCPLeaseMissingDhclientErrorz$Raised when unable to find dhclient.N)rrrrrrrrrQsrc@seZdZdZdS)NoDHCPLeaseMissingUdhcpcErrorz)Raised when unable to find udhcpc client.N)rrrrrrrrrUsrc CsZxN|jD]D}y|}tjd|j|Sttfk rJtjd|jYqXqWtdS)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. zDHCP client selected: %szDHCP client not found: %sN)Zdhcp_client_priorityLOGdebug client_namerrwarning)distroclientZ dhcp_clientrrrselect_dhcp_clientYs rcCsZ|dkr(t}|dkrDtjdtn|tkrDtjd|tt|}|j|||S)aPerform 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 rrrr rdhcp_discovery)rZnic dhcp_log_funcrrrrmaybe_perform_dhcp_discoveryks   r"cCsttjt|ddS)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)Zcontentrrrnetworkd_parse_leasesr%cCsT|dkr t}i}tjj|s |Sx.tj|D] }ttjtjj||||<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_DIRospathisdirlistdirr%r load_filejoin)leases_dretZlfilerrrnetworkd_load_leasess r/cCsF|dkr t}t|d}x*t|jD]\}}|j|r$||Sq$WdS)N)r-)r&r/sorteditemsget)Zkeynamer-ZleasesZ_ifindexdatarrrnetworkd_get_option_from_leasess   r4c@sLeZdZdZeddZeddZeedddZeedd d Z d S) DhcpClientcCstjd|jgddgddS)NZpkillr)rcs)rr)clsrrrkill_dhcp_clientszDhcpClient.kill_dhcp_clientcCs.|jtjd}x|D]}tj|qWdS)Nz/var/lib/dhcp/*)r:globr'remove)r9filesfilerrr clear_leasess  zDhcpClient.clear_leases)dhcp_interfacecCs|jd|j|ddgddS)Nstartrr7)r8)manage_servicer)r9r@rrrr start_serviceszDhcpClient.start_servicecCs|jd|jddgddS)Nstoprr7)r8)rBr)r9r@rrrr stop_serviceszDhcpClient.stop_serviceN) rrrr classmethodr:r?strrCrErrrrr5s  r5c@steZdZdZddZeeeeee fdddZ ddd Z ed d Z ed d Z edddZeddZdS) IscDhclientdhclientcCs&tjd|_|js"tjdtdS)NrIz7Skip dhclient configuration: No dhclient command found.)rwhich dhclient_pathrrr)selfrrr__init__s  zIscDhclient.__init__) lease_filereturncCstjdtj}g}tj|}t|dkr6tdj|xh|j|D]Z}g}xB|j dD]4}|j j ddj dd}|sxqV|j |j dd qVW|j t |qBW|std j||S) a7Parse 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. zlease {(?P.*?)}\nrz&Cannot parse empty dhcp lease file {0};"r6zoption  r7z1Cannot parse dhcp lease file {0}. No leases found)recompileDOTALLrr+lenrformatfindallsplitstripreplaceappendr#)rNZ lease_regexZ dhcp_leasesZ lease_contentZleaseZ lease_optionslinerrrparse_dhcp_lease_files&    z!IscDhclient.parse_dhcp_lease_fileNc!Cstjd|d}d}d}tjttj|tj|WdQRX|jj|t |rdt |dd}d||f}t j dd } tj j| |d }tj||y"tj|j|j||||\} } WnBtjk r} z"tjd | j| j| jt| WYdd} ~ XnXtj||gd d d} | r@tjddjdd| DgSd}d}xtddD]v}tj|j}y t|}Wntk rYn6Xtj |}|dkrtjd|tj!|t"j#d}Pt$j%d qTW|stj&d||d|dk r|| | |j'|S)aRun 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.leaseNz20:%s$z0interface "%s" {send dhcp-client-identifier %s;}T) needs_exez-dhclient.confz3dhclient exited with code: %s stderr: %r stdout: %rg{Gz?)ZmaxwaitZnaplenz+dhclient did not produce expected files: %sz, css|]}tjj|VqdS)N)r'r(basename).0frrr Isz-IscDhclient.dhcp_discovery..unknownFrir7zkilling dhclient with pid=%szCdhclient(pid=%s, parentpid=%s) failed to daemonize after %s secondsg$@)(rr contextlibsuppressFileNotFoundErrorr'r<net_opslink_upr r rget_tmp_ancestorr(r,r write_filerZbuild_dhclient_cmdrKProcessExecutionError exit_codestderrstdoutrZwait_for_filesrranger+rZint ValueErrorZ get_proc_ppidkillsignalSIGKILLtimeZsleeperrorr^)rL interfacer!rZpid_filerNZ config_filedhcp_client_identifierZinterface_dhclient_contenttmp_dirouterrryZmissingZppidZ daemonized_Z pid_contentpidrrrr sz              zIscDhclient.dhcp_discoveryc sjdddtjdD}g}fdd}d}xt|D]\}}||krTq@t|}|tdd krd }t||d |kr|||t||d |Sd j||d |d} d j||d||} ||}n |tddkrjd}t||d |kr$|||t||d |Sd j||d |ddg} d j||d||} ||}n|td dkrd}t||d |kr|||t||d |Sd j||d |dddg} d j||d||} ||}n|td d krd}t||d |kr>|||t||d |Sd j||d |ddddg} d j||d||} ||}nx|dkrd}t||d |kr|||t||d |Sd} d j||d ||} ||}ntj d||S|j d| |f| fq@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 rPcSsg|] }|r|qSrr)rctokrrr sz3IscDhclient.parse_static_routes..z[, .]csd|||f}tj|dS)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)rry)ZcidrZrequiredZremainmsg)rfc3442rr _trunc_errorsz5IscDhclient.parse_static_routes.._trunc_errorr! N.r7ra0z0.0.0.0zSParsed invalid net length "%s". Verify DHCP rfc3442-classless-static-routes value.z%s/%s) rstriprSrY enumeratersrrrVr,rryr\) rtokens static_routesrZ current_idxidxrZ net_lengthZreq_toksZ net_addressZgatewayr)rrparse_static_routesjsr!     "     zIscDhclient.parse_static_routescCsJdddg}x:|D]2}tjj|rttj|dkrtjd||SqWdS)Nz/var/lib/dhclientz /var/lib/dhcpz/var/lib/NetworkManagerrzUsing %s lease directory)r'r(existsrVr*rr)Zsupported_dirsdrrrget_dhclient_ds  zIscDhclient.get_dhclient_dcCs~|dkrtj}|sdStj|}d}d}xN|D]F}|jdr@q0|jdsLq0tjj||}tjj|}||kr0|}|}q0W|S)Nr7Z dhclient6.lease.leases)rr) rHrr'r* startswithendswithr(r,getmtime)Zlease_dZ lease_filesZ latest_mtimeZ latest_fileZfnameZabs_pathmtimerrrget_latest_leases$     zIscDhclient.get_latest_leasec Csdt|dP}xH|D]@}d|kr|jdjd}t|dkr|d}tjd||}qWWdQRX|S)Nrzdhcp-server-identifierz ; rRrzFound DHCP identifier %s)openrZrYrVrr)rNfdr]ZwordsZdhcptokZlatest_addressrrr!parse_dhcp_server_from_lease_files    z-IscDhclient.parse_dhcp_server_from_lease_file)NN)N)rrrrrM staticmethodrGrrrr^r rrrrrrrrrHs$ r k  rHc@seZdZdZddZdS)DhcpcdZdhcpcdcCs tddS)NzDhcpcd not yet implemented)r)rLrrrrMszDhcpcd.__init__N)rrrrrMrrrrrsrc@s"eZdZdZddZdddZdS)UdhcpcudhcpccCs&tjd|_|js"tjdtdS)Nrz3Skip udhcpc configuration: No udhcpc command found.)rrJ udhcpc_pathrrr)rLrrrrMs  zUdhcpc.__init__NcCstjd|tjdd}tjj||d}tjt tj |WdQRX|j j |tjj|d}t j|td|jdd d |d |d d ddg }t|rt|dd}|jdd|jddgytj|d|idd\} } WnBtjk r} z"tjd| j| j| jt| WYdd} ~ XnX|dk r0|| | t jt j|} | dj} | r~ddt| ddd| dddD| d<| gS)asRun 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_scriptiz-OZ staticroutesz-iz-sz-nz-qz-fz-v)Zethernet_formatz-xz0x3d:%s:r6Z LEASE_FILE)Z update_envZcapturez1udhcpc exited with code: %s stderr: %r stdout: %rrcSsg|]}|qSrr)rcirrrrksz)Udhcpc.dhcp_discovery..rr7)rrrrlr'r(r,rgrhrir<rjrkrrm UDHCPC_SCRIPTrr r extendr[rrnrorprqrZ load_jsonr+rYzip)rLrzr!rr|rNrcmdr{r}r~ryZ lease_jsonrrrrr !sR        ,zUdhcpc.dhcp_discovery)NN)rrrrrMr rrrrrs r)NN)N)N).abcrgr;Zloggingr'rSrvrxiorZtypingrrrr$Z cloudinitrrrZ cloudinit.netr r r r r Z getLoggerrrr&r Exceptionrrrrrrr"r%r/r4ABCr5rHrrrrrrs>     L