o x[hV@sdZddlZddlZddlmZddlmZmZmZm Z m Z m Z m Z ddl mZddlmZddlmZmZddlmZddlmZmZeeZGdd d ZGd d d ZGd d d ZGdddZde ee efde e fddZ!dS)z.Module for ephemeral network context managers N)partial)AnyCallableDictListLiteralOptionalTuple)NoDHCPLeaseErrormaybe_perform_dhcp_discovery)ProcessExecutionError)UrlError wait_for_urlc@sLeZdZdZ  ddefddZddZdd Zd d Zd d Z ddZ dS)EphemeralIPv4NetworkaContext manager which sets up temporary static network configuration. No operations are performed if the provided interface already has the specified configuration. This can be verified with the connectivity_urls_data. If unconnected, bring up the interface with valid ip, prefix and broadcast. If router is provided setup a default route for that interface. Upon context exit, clean up the interface leaving no configuration behind. Ninterface_addrs_before_dhcpc Cst||||gstd||||zt||_Wnty.} ztd| | d} ~ ww||_||_||_||_ ||_ g|_ ||_ |jd|j|_ ||ji|_dS)aSetup context manager and validate call signature. @param interface: Name of the network interface to bring up. @param ip: IP address to assign to the interface. @param prefix_or_mask: Either netmask of the format X.X.X.X or an int prefix. @param broadcast: Broadcast address for the IPv4 network. @param router: Optionally the default gateway IP. @param static_routes: Optionally a list of static routes from DHCP z5Cannot init network on {0} with {1}/{2} and bcast {3}z4Cannot setup network, invalid prefix or netmask: {0}N/)all ValueErrorformatnetipv4_mask_to_net_prefixprefix interfaceip broadcastrouter static_routes cleanup_cmdsdistrocidrgetr) selfrrrprefix_or_maskrrrrer$9/usr/lib/python3/dist-packages/cloudinit/net/ephemeral.py__init__s8 zEphemeralIPv4Network.__init__c Csz?z|Wn!ty(}zdt|jvrdt|jvrWYd}~nd}~ww|jr3|WdS|jr=|WdSWdStyN|dddw)zSet up ephemeral network if interface is not connected. This context manager handles the lifecycle of the network interface, addresses, routes, etc z File existszAddress already assignedN) _bringup_devicer strstderrr_bringup_static_routesr_bringup_router__exit__)r!r#r$r$r% __enter__Ns(  zEphemeralIPv4Network.__enter__cCs|jD]}|qdS)zTeardown anything we set up.N)r)r! excp_type excp_valueexcp_tracebackcmdr$r$r%r,rs zEphemeralIPv4Network.__exit__cCs:td|j|j|jt|ji}|d}|jd}|j dd|diDv}|j dd|jdiDv}|rHtd|j|j n |j j |j|j|j|r^td|jn |j j j |jd d |rrtd |jn|jt|j j j|jd d |rtd |j d S|jt|j j j|j|jd S)afPerform the ip commands to fully set up the device. Dhcp clients behave differently in how they leave link state and ip address assignment. Attempt assigning address and setting up link if needed to be done. Set cleanup_cmds to return the interface state to how it was prior to execution of the dhcp client. z:Attempting setup of ephemeral network on %s with %s brd %supcSg|]}|dqSrr .0rr$r$r% s z8EphemeralIPv4Network._bringup_device..ipv4cSr3r4r5r6r$r$r%r8sz1Skip adding ip address: %s already has address %sz9Skip bringing up network link: interface %s is already upinet)familyzLNot queueing link down: link [%s] was up prior before receiving a dhcp leasezSNot queueing address removal: address %s was assigned before receiving a dhcp leaseN)LOGdebugrrrnetinfo netdev_infor rrrnet_opsadd_addrlink_uprappendr link_downdel_addr)r!interface_addrs_after_dhcphas_linkhad_linkhas_iphad_ipr$r$r%r'wsl        z$EphemeralIPv4Network._bringup_devicec CsJ|jD]\}}|jj|j|||jdt|jjj|j||dqdS)Nrgateway) rrr@ append_routerrinsertr del_route)r! net_addressrLr$r$r%r*sz+EphemeralIPv4Network._bringup_static_routesc Cs|jj}d|vrtd|j|dS|jjj|j|j|j d|j dt |jjj |j|j|j d|jjj|jd|jd|j dt |jjj |jddS)zr?r)r!rrercrhr$r$r%r&s zEphemeralDHCPv4.__init__cCs&t|j}rtd|dS|S)zUSetup sandboxed dhcp context, unless connectivity_url can already be reached.z:Skip ephemeral DHCP setup, instance has connectivity to %sN)_check_connectivity_to_imdsrcr<r= obtain_lease)r!imds_reached_at_urlr$r$r%r-(szEphemeralDHCPv4.__enter__cCs |dS)z Teardown sandboxed dhcp context.N) clean_network)r!r.r/r0r$r$r%r,7s zEphemeralDHCPv4.__exit__cCs$d|_|jr|jddddSdS)z@Exit _ephipv4 context to teardown of ip configuration performed.N)rgrfr,r_r$r$r%rl;szEphemeralDHCPv4.clean_networkcCs|jr|jSt|j|j|j|_|jsttd|jd|jd|jdddddgddd}||}|d sGt |d |d |d <|d rV|jj |d |d <t |jfd |ji|}|||_|jS)a9Perform dhcp discovery in a sandboxed environment if possible. @return: A dict representing dhcp options on the most recent lease obtained from the dhclient discovery if run, otherwise an error is raised. @raises: NoDHCPLeaseError if no leases could be obtained. z#Received dhcp lease on %s for %s/%srz fixed-addressz subnet-maskzbroadcast-address)zrfc3442-classless-static-routeszclassless-static-routesrz unknown-121routers)rrr"rrrrr"rrr)rgr rrerhr r<r=extract_dhcp_options_mappingrmask_and_ipv4_to_bcast_addr dhcp_clientparse_static_routesrrr-rf)r!nmapkwargsephipv4r$r$r%rjAsP    zEphemeralDHCPv4.obtain_leasecCsDi}|D]\}}t|tr||||q|j|||<q|Srd)items isinstancelistget_first_option_valuergr )r!rrresultinternal_referencelease_option_namesr$r$r%rnxs z,EphemeralDHCPv4.extract_dhcp_options_mappingcCs(|D]}||s|j|||<qdSrd)r rg)r!internal_mappingr{rydifferent_namesr$r$r%rxs  z&EphemeralDHCPv4.get_first_option_value)NNN)rWrXrYrrrr(rr&r-r,rlrjrnrxr$r$r$r%rbs 7 rbc @speZdZdZ   ddededeeeee ffdd Z d d Z d e d de eeeffddZddZdS)EphemeralIPNetworkahCombined ephemeral context manager for IPv4 and IPv6 Either ipv4 or ipv6 ephemeral network may fail to initialize, but if either succeeds, then this context manager will not raise exception. This allows either ipv4 or ipv6 ephemeral network to succeed, but requires that error handling for networks unavailable be done within the context. FTNipv6r9rccCs2||_||_||_t|_d|_||_||_dS)a Args: distro: The distro object interface: The interface to bring up ipv6: Whether to bring up an ipv6 network ipv4: Whether to bring up an ipv4 network connectivity_urls_data: List of url data to use for connectivity check before attempting to bring up ephemeral networks. If connectivity can be established to any of the urls, then the ephemeral network setup is skipped. N) rr9r contextlib ExitStackstack state_msgrrc)r!rrrr9rcr$r$r%r&s  zEphemeralIPNetwork.__init__cCs|js|js|Sg}d}t|j}rtd||Std|jr6|jdd\}}||O}|r6|||jrL|jdd\}}||O}|rL|||jrQ|sT|jsWd|_|sbt d|d |S) NFz:We already have connectivity to IMDS at %s, skipping DHCP.z/No connectivity to IMDS, attempting DHCP setup.r9) ip_versionrzusing link-local ipv6zGFailed to bring up EphemeralIPNetwork. Datasource setup cannot continuer) r9rrircr<r= _perform_ephemeral_network_setuprCrerror)r! exceptionsephemeral_obtainedimds_urlipv4_ephemeral_obtainedipv4_exceptionipv6_ephemeral_obtainedipv6_exceptionr$r$r%r-sB      zEphemeralIPNetwork.__enter__r)r9rreturnc Csz3|dkr|jt|j|jdn|dkr"|jt|j|jntd|td|j|WdSt t fyS}ztd|j|d|fWYd }~Sd }~ww) a Attempt to bring up an ephemeral network for the specified IP version. Args: ip_version (str): The IP version to bring up ("ipv4" or "ipv6"). Returns: Tuple: A tuple containing: - a boolean indicating whether an ephemeral network was successfully obtained - an optional exception if ephemeral network setup failed or None if successful r9)rrerzUnsupported IP version: z7Successfully brought up %s for ephemeral %s networking.)TNz2Failed to bring up %s for ephemeral %s networking.FN) r enter_contextrbrrr\rr<r=r r )r!rr#r$r$r%rs>z3EphemeralIPNetwork._perform_ephemeral_network_setupcGs|jdSrd)rcloser`r$r$r%r,szEphemeralIPNetwork.__exit__)FTN)rWrXrYrZboolrrrr(rr&r-rr Exceptionrr,r$r$r$r%r~s&  4 1r~rcrc sfdd}stddStddDpd}ztdd D||d d d \}}WntyE}z td |WYd}~dSd}~ww|sOtddS|S)a Perform a connectivity check to the provided URLs to determine if the ephemeral network setup is necessary. This function attempts to reach one of the provided URLs and returns the URL that was successfully reached. If none of the URLs can be reached, it returns None. The timeout for the request is determined by the highest timeout value provided in the connectivity URLs data. If no timeout is provided, a default timeout of 5 seconds is used. Args: connectivity_urls_data: A list of dictionaries, each containing the following keys: - "url" (str): The URL to check connectivity for. - "headers" (dict, optional): Headers to include in the request. - "timeout" (int, optional): Timeout for the request in seconds. Returns: Optional[str]: The URL that was successfully reached, or None if no connectivity was established. csfddDd}|S)z Helper function to get headers for a given URL from the connectivity URLs data provided to _check_connectivity_to_imds. cs"g|] }|dkr|dqSurlheadersr5r7url_datarr$r%r88s  zD_check_connectivity_to_imds.._headers_cb..rr$rrcrr% _headers_cb3s z0_check_connectivity_to_imds.._headers_cbzZNo connectivity URLs provided. Skipping connectivity check before ephemeral network setup.Ncss|] }|ddVqdS)timeoutrNr5rr$r$r% Hsz._check_connectivity_to_imds..cSsg|]}|dqSrr$rr$r$r%r8Nsz/_check_connectivity_to_imds..Fr)urls headers_cbrconnect_synchronouslymax_waitz8Failed to reach IMDS without ephemeral network setup: %sz5Failed to reach IMDS without ephemeral network setup.)r<r=maxrr )rcrrurl_that_worked_r#r$rr%ris:     ri)"rZrlogging functoolsrtypingrrrrrrr cloudinit.netrcloudinit.netinfor>cloudinit.net.dhcpr r cloudinit.subpr cloudinit.url_helperr r getLoggerrWr<rr\rbr~r(rir$r$r$r%s, $    f s