o x[hЏ@sddlZddlZddlZddlZddlZddlZddlZddlZddlZddl m Z ddl m Z ddl mZddlmZmZmZmZmZmZddlZddlmZmZmZddlmZmZeeZ dZ!d Z"d Z#d Z$Gd d d e%Z&Gddde&Z'Gddde&Z(Gddde&Z) d%dee*effddZ+ddZ,d&ddZ-d&ddZ.Gdddej/Z0Gdd d e0Z1Gd!d"d"e0Z2Gd#d$d$e0Z3e2e1e3gZ4dS)'N)suppress)StringIO)TimeoutExpired)AnyCallableDictListOptionalTuple)subp temp_utilsutil)get_interface_macis_ib_interfacez/run/systemd/netif/leasesz/var/lib/dhclientz .+\.leases?$aN#!/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@eZdZdZdS)NoDHCPLeaseErrorz'Raised when unable to get a DHCP lease.N__name__ __module__ __qualname____doc__rr4/usr/lib/python3/dist-packages/cloudinit/net/dhcp.pyrArc@r)InvalidDHCPLeaseFileErrorzRaised when parsing an empty or invalid dhclient.lease file. Current uses are DataSourceAzure and DataSourceEc2 during ephemeral boot to scrape metadata. NrrrrrrErrc@r)NoDHCPLeaseInterfaceErrorz7Raised when unable to find a viable interface for DHCP.NrrrrrrMrrc@r)NoDHCPLeaseMissingDhclientErrorz$Raised when unable to find dhclient.NrrrrrrQrrreturncCs2|p|j}|durtdt|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.)fallback_interfaceLOGdebugr dhcp_clientdhcp_discovery)distronic dhcp_log_func interfacerrrmaybe_perform_dhcp_discoveryUs  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) list_values)dict configobj ConfigObjr)contentrrrnetworkd_parse_leaselsr-cCsP|durt}i}tj|s|St|D]}tttj||||<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_DIRospathisdirlistdirr-r load_text_filejoin)leases_dretlfilerrrnetworkd_load_leasesws  r8cCsF|durt}t|d}t|D]\}}||r ||SqdS)N)r5)r.r8sorteditemsget)keynamer5leases_ifindexdatarrrnetworkd_get_option_from_leasess   r@c @seZdZdZdZddZeddZeddZed e fd d Z ed e fd d Z e j de dee effddZee j de deee e ffddZe j  dde deedee effddZdS) DhcpClient cCst|j|_|js tdSN)r which client_namedhcp_client_pathrselfrrr__init__szDhcpClient.__init__cCstjd|jgddgddS)Npkillrrcs)r rF)clsrrrkill_dhcp_clientszDhcpClient.kill_dhcp_clientcCs*|td}|D]}t|q dS)Nz/var/lib/dhcp/*)rPglobr/remove)rOfilesfilerrr clear_leasess   zDhcpClient.clear_leasesdhcp_interfacecCs|jd|j|ddgddS)NstartrrLrMmanage_servicerFrOrVr#rrr start_services zDhcpClient.start_servicecCs|jd|jddgddS)NstoprrLrMrXrZrrr stop_serviceszDhcpClient.stop_servicer&rcCiS)zGet the most recent lease from the ephemeral phase as a dict. Return a dict of dhcp options. The dict contains key value pairs from the most recent lease. rrIr&rrrget_newest_leaseszDhcpClient.get_newest_leaseroutescCsgS)ap parse classless static routes from string The tuple is composed of the network_address (including net length) and gateway for a parsed static route. @param routes: string containing classless static routes @returns: list of tuple(str, str) for all valid parsed routes until the first parsing error. r)rarrrparse_static_routess zDhcpClient.parse_static_routesNr%cCr^)aRun dhcp client on the interface without scripts or filesystem artifacts. @param interface: Name of the network interface on which to send a dhcp request @param dhcp_log_func: A callable accepting the client output and error streams. @param distro: a distro object for network interface manipulation @return: dict of lease options representing the most recent dhcp lease parsed from the dhclient.lease file r)rIr&r%r#rrrr"szDhcpClient.dhcp_discoveryNN)rrrrFtimeoutrJ classmethodrPrUstrr[r]abcabstractmethodrrr` staticmethodrr rbr rr"rrrrrAs6  "  rAc seZdZdZfddZededeeee ffddZ edd Z d edeee ffd d Z dd ede edeee ffddZededeeeeffddZede efddZdefddZZS) IscDhclientdhclientctd|_dS)Nz/run/dhclient.leasesuperrJ lease_filerH __class__rrrJ  zIscDhclient.__init__ lease_contentrcCstdtj}g}|s gS||D]:}g}|dD]}|dddd}|r4||ddqt|}| d}|rGt ||d<||q|S) zparse the content of a lease file @param lease_content: a string containing the contents of an isc-dhclient lease @return: a list of leases, most recent last zlease {(?P.*?)}\n;"rBzoption  rL unknown-245) recompileDOTALLfindallsplitstripreplaceappendr)r;rjget_ip_from_lease_value)rs lease_regex dhcp_leaseslease lease_optionslineoptionsopt_245rrr parse_leasess&  zIscDhclient.parse_leasescCsx|dd}t|dkr2d}|dD]}t|dkrd|}||7}qtdt|ddd}n|d }t|S) N\rB:rL0z>Lzutf-8) r~lenr|structpackintencodesocket inet_ntoa)fallback_lease_valueunescaped_value hex_stringhex_pair packed_bytesrrrrs      z#IscDhclient.get_ip_from_lease_valuer&cCsrtt+t|j}|r||}|r'|dWdSWdiSWdiS1s2wYiS)aGet the most recent lease from the ephemeral phase as a dict. Return a dict of dhcp options. The dict contains key value pairs from the most recent lease. @param interface: an interface string - not used in this class, but required for function signature compatibility with other classes that require a distro object @raises: InvalidDHCPLeaseFileError on empty or unparsable leasefile content. N)rFileNotFoundErrorr r3ror)rIr&r,rrrrr`s    zIscDhclient.get_newest_leaseNr%c Cshtd|d}d}d}t|j|}t|jd}ttt|t|jWdn1s4wY|j |t |rhdt |dd} d|| f} t jd d } tj| |d }t|| zt||j|j|||\} } Wntjy}ztd |j|j|jt|d}~wwtj||jg|dd }|rtdddd|DiSd}d}d}d}t|D]M}z t|}t|}Wntyd|d}Yn*t yd|d}Ynw|!|}|dkrtd|t"|t#j$d }n t%&|qt||st'd||d|dur'|| | |(|}|r1|St))aRun dhclient on the interface without scripts/filesystem artifacts. @param interface: Name of the network interface on which to send a dhcp request @param dhcp_log_func: A callable accepting the dhclient output and error streams. @param distro: a distro object for network interface manipulation @return: dict of lease options representing the most recent dhcp lease parsed from the dhclient.lease file !Performing a dhcp discovery on %sz/run/dhclient.pidN{Gz?z20:%s$z0interface "%s" {send dhcp-client-identifier %s;}T needs_exez-dhclient.confz3dhclient exited with code: %s stderr: %r stdout: %r)maxwaitnaplenz+dhclient did not produce expected files: %sz, css|] }tj|VqdSrD)r/r0basename).0frrr }sz-IscDhclient.dhcp_discovery..unknownFrBNo PID file found at z, dhclient is still runningPID file contained [z], dhclient is still runningrLzkilling dhclient with pid=%szCdhclient(pid=%s, parentpid=%s) failed to daemonize after %s secondsg$@)*rr rrdrrr/rRronet_opslink_uprrr get_tmp_ancestorr0r4r write_filer build_dhclient_cmdrGProcessExecutionError exit_codestderrstdoutrwait_for_fileswarningranger3r} ValueError get_proc_ppidkillsignalSIGKILLtimesleeperrorr`r)rIr&r%r#pid_file config_file sleep_time sleep_cyclesrdhcp_client_identifierinterface_dhclient_contenttmp_dirouterrrmissingppid daemonized pid_content debug_msg_pidrrrrr"&s                       zIscDhclient.dhcp_discoveryrac s8|dddtdD}g}dvrdd|D}fdd}d }t|D]o\}}||kr3q)t|}|td d vrzd }t||d |krZ|||t||d |Sd||d|d} d||d||} ||}n|tdd vrd}t||d |kr|||t||d |Sd||d|ddg} d||d||} ||}n|td dvrd}t||d |kr|||t||d |Sd||d|dddg} d||d||} ||}n|tdd vrNd}t||d |kr+|||t||d |Sd||d|dgd} d||d||} ||}n@|d krd}t||d |krp|||t||d |Sd} d||d||} ||}n t d||S| d| |f| fq)|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") ] # unknown-121 option format sr3 = parse_static_routes( "0:a:0:0:1:20:a8:3f:81:10:a:0:0:1:20:a9:fe:a9:fe:a:0:0:1") sr3 = [ ("0.0.0.0/0", "10.0.0.1"), ("168.63.129.16/32", "10.0.0.1"), ("169.254.169.254/32", "10.0.0.1"), ] Python version of isc-dhclient's hooks: /etc/dhcp/dhclient-exit-hooks.d/rfc3442-classless-routes rtcSsg|]}|r|qSrrrtokrrr sz3IscDhclient.parse_static_routes..z[, . :]rcSsg|] }tt|dqS)r)rfrrrrrrscsd|||f}t|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)rr)cidrrequiredremainmsgrfc3442rr _trunc_errors  z5IscDhclient.parse_static_routes.._trunc_errorr! N.rLrrr)rrrz0.0.0.0zSParsed invalid net length "%s". Verify DHCP rfc3442-classless-static-routes value.z%s/%s) rstriprxr| enumeraterrrr4rrr) ratokens static_routesr current_idxidxr net_lengthreq_toks net_addressgatewayrrrrbs| *     "    zIscDhclient.parse_static_routesc Csd}|j|jfttffD]C\}}|sq g}zt|}Wn ty%Yq wd}|D]}t||s3q*tj ||}tj |}||krH|}|}q*|rO|Sq dS)zGet the latest lease file from a distro-managed dhclient Doesn't consider the ephemeral timeframe lease. @param distro: used for distro-specific lease location and filename @return: The most recent lease file, or None Ng) dhclient_lease_directorydhclient_lease_file_regexDHCLIENT_FALLBACK_LEASE_DIRDHCLIENT_FALLBACK_LEASE_REGEXr/r2rrxsearchr0r4getmtime) r# latest_file directoryregex lease_files latest_mtimefnameabs_pathmtimerrr!get_newest_lease_file_from_distro%s8     z-IscDhclient.get_newest_lease_file_from_distrokeycCsR||}|r#t|}|r%t||D]}||}|r"|SqdSdSdS)a8Get a key from the latest lease from distro-managed dhclient Doesn't consider the ephemeral timeframe lease. @param lease_dir: distro-specific lease to check @param lease_file_regex: distro-specific regex to match lease name @return: The most recent lease file, or None N)rr r3reversedrr;)rIr#rror,rserverrrrget_key_from_latest_leaseQs   z%IscDhclient.get_key_from_latest_leaserc)rrrrFrJrirfrrrrrr`r rr"r rbrr __classcell__rrrprrjs.      w+rjc @seZdZdZdZ  ddedeedeee ffddZ e d e d e dee fd d Ze d ededefddZdedeee ffddZe dedeeeeffddZdS)Dhcpcddhcpcdi,Nr&r%rc Cstd|d}t|j|}g}|j|zt|rdg}|jdddddg||}tj||jd \}} |d ur?||| | |} | rtg|d j } d } d } d }t |D]b}z't |  } t| }||} | rtd|| t| tjW| WSWn.tytd| Y| WStyd| d}Yntyd| d}Ynw| WSt|q\t|| WStdty}ztd|j|j|j t|d }~wtjy}ztd|j|j|j t|d }~ww)aRun dhcpcd on the interface without scripts/filesystem artifacts. @param interface: Name of the network interface on which to send a dhcp request @param dhcp_log_func: A callable accepting the client output and error streams. @param distro: a distro object for network interface manipulation @return: dict of lease options representing the most recent dhcp lease parsed from the dhclient.lease file rrz --clientid --ipv4onlyz--waitipz --persistentz--noarpz--script=/bin/true)rdNz-PFrBz!killing dhcpcd with pid=%s gid=%sz9Process group id [%s] has already exited, nothing to killrz, dhcpcd is still runningrz], dhcpcd is still runningzNo lease foundz8dhcpcd timed out after %s seconds: stderr: %r stdout: %r1dhcpcd exited with code: %s stderr: %r stdout: %r)rr rrdrrrrFr r`rr}rr r3 get_proc_pgidr/killpgrrProcessLookupErrorrrrrrrrrr)rIr&r%r#rrinfiniband_argumentcommandrrrrrgidrrrrrrrr"hs                zDhcpcd.dhcp_discoveryr?dhcp_option_numbercCs>d}dtdtfdd}|||D] \}}||kr|SqdS)aVget a specific option from a binary lease file This is required until upstream dhcpcd supports unknown option 245 upstream bug: https://github.com/NetworkConfiguration/dhcpcd/issues/282 @param data: Binary lease data @param number: Option number to return @return: the option (bytes) or None r?indexcsslt||dkr4||}|d|}|d|d||}||fVd||}t||dks dSdS)zoptions are variable length, and consist of the following format option number: 1 byte option length: 1 byte option data: variable length (see length field) rrLN)r)r?r codelengthoptionrrr iter_optionss   z>Dhcpcd.parse_unknown_options_from_packet..iter_optionsN)bytesr)r?r INDEXrrrrrr!parse_unknown_options_from_packetsz(Dhcpcd.parse_unknown_options_from_packet lease_dumpc Cstd||z$tdd|dddD}|s*d}t||t||Wnty@}z td|t|d }~ww||d <d d | D}d dd}| D]\}}||vrf| |||<qWt d|d}t |d} | rt| |d<|S)a`parse the output of dhcpcd --dump map names to the datastructure we create from dhclient example dhcpcd output: broadcast_address='192.168.15.255' dhcp_lease_time='3600' dhcp_message_type='5' dhcp_server_identifier='192.168.0.1' domain_name='us-east-2.compute.internal' domain_name_servers='192.168.0.2' host_name='ip-192-168-0-212' interface_mtu='9001' ip_address='192.168.0.212' network_number='192.168.0.0' routers='192.168.0.1' subnet_cidr='20' subnet_mask='255.255.240.0' z)Parsing dhcpcd lease for interface %s: %rcSs"g|] }d|vr|jdddqS)=rL)maxsplit)r|)rarrrr(s  z-Dhcpcd.parse_dhcpcd_lease..'rB z;No valid DHCP lease configuration found in dhcpcd lease: %rzError parsing dhcpcd lease: %rNr&cSsi|] \}}|dd|qS)r-)r~)rrvaluerrr =sz-Dhcpcd.parse_dhcpcd_lease..z fixed-addressr)z ip-addresszclassless-static-routesz/var/lib/dhcpcd/z.leaserw)rr r)r}r~r|rrrr:popr load_binary_filerrrr) rr&rrrname_mapsource destination dhcp_messagerrrrparse_dhcpcd_lease sH      zDhcpcd.parse_dhcpcd_leasec CsXz|t|jdd|gj|WStjy+}ztd|j|j|jt |d}~ww)zReturn a dict of dhcp options. @param interface: which interface to dump the lease from @raises: InvalidDHCPLeaseFileError on empty or unparsable leasefile content. z --dumpleaserrN) r%r rFrrrr rrr)rIr&rrrrr`Ts, zDhcpcd.get_newest_leaseracCsD|}|rddt|ddd|dddDStd|gS)a classless static routes as returned from dhcpcd --dumplease 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. @param routes: string containing classless static routes @returns: list of tuple(str, str) for all valid parsed routes until the first parsing error. e.g.: sr=parse_static_routes( "0.0.0.0/0 10.0.0.1 168.63.129.16/32 10.0.0.1" ) sr=[ ("0.0.0.0/0", "10.0.0.1"), ("169.63.129.16/32", "10.0.0.1"), ] cSg|]}|qSrrrirrrrz.Dhcpcd.parse_static_routes..NrrLz'Malformed classless static routes: [%s])r|ziprrrarrrrrbqs ( zDhcpcd.parse_static_routesrc)rrrrFrdrfr rrrr"rirrrr%r`rr rbrrrrrds2  w +H$rc seZdZdZfddZ  ddedeedeee ffdd Z dedeee ffd d Z e d ede eeeffd dZZS)Udhcpcudhcpccrl)NrBrmrHrprrrJrrzUdhcpc.__init__Nr&r%rc CsFtd|tjdd}tj||d|_tt t |jWdn1s+wY|j |tj|d}t |td|jdd d |d |d d ddg }t|rj|ddt|ddddgztj|d|jidd\}}Wntjy} ztd| j| j| jt| d} ~ ww|dur|||||S)arRun 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. rTrz .lease.jsonN udhcpc_scriptiz-O staticroutesz-iz-sz-nz-qz-fz-vz-xz 0x3d:20{}rrrB LEASE_FILE) update_envcapturez1udhcpc exited with code: %s stderr: %r stdout: %r)rr r rr/r0r4rorrrRrrr r UDHCPC_SCRIPTrFrextendformatrr~r rrrrrr`) rIr&r%r#rr.cmdrrrrrrr"s\       zUdhcpc.dhcp_discoverycCstt|jS)aGet the most recent lease from the ephemeral phase as a dict. Return a dict of dhcp options. The dict contains key value pairs from the most recent lease. @param interface: an interface name - not used in this class, but required for function signature compatibility with other classes that require a distro object @raises: InvalidDHCPLeaseFileError on empty or unparsable leasefile content. )r load_jsonr3ror_rrrr`s zUdhcpc.get_newest_leaseracCs8|}|rddt|ddd|dddDSgS)NcSr&rrr'rrrrr)z.Udhcpc.parse_static_routes..rrL)r|r*r+rrrrbs(zUdhcpc.parse_static_routesrc)rrrrFrJrfr rrrr"r`rirr rbrrrrprr,s   H(r,rcrD)5rgrQloggingr/rxrrrr contextlibrior subprocessrtypingrrrrr r r* cloudinitr r r cloudinit.netrr getLoggerrrr.rrr3 Exceptionrrrrrfr'r-r8r@ABCrArjrr,ALL_DHCP_CLIENTSrrrrsT     !    K.f