
"T`=                 @   sx  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 m Z d  d l m Z d  d l m Z d  d l m Z d  d l m Z e j e  Z d Z Gd	 d
   d
 e  Z Gd d   d e  Z Gd d   d e  Z d d d d  Z d d   Z d d d  Z d d   Z  d d d  Z! d d d  Z" d d   Z# d S)    N)StringIO)EphemeralIPv4Networkfind_fallback_nicget_devicelisthas_url_connectivity)mask_and_ipv4_to_bcast_addr)
temp_utils)subp)utilz/run/systemd/netif/leasesc               @   s   e  Z d  Z d Z d S)InvalidDHCPLeaseFileErrorzRaised when parsing an empty or invalid dhcp.leases file.

    Current uses are DataSourceAzure and DataSourceEc2 during ephemeral
    boot to scrape metadata.
    N)__name__
__module____qualname____doc__ r   r   4/usr/lib/python3/dist-packages/cloudinit/net/dhcp.pyr      s   r   c               @   s   e  Z d  Z d Z d S)NoDHCPLeaseErrorz'Raised when unable to get a DHCP lease.N)r   r   r   r   r   r   r   r   r   $   s   r   c               @   sm   e  Z d  Z d d d d d  Z d d   Z d d   Z d d	   Z d
 d   Z d d   Z d d   Z	 d S)EphemeralDHCPv4Nc             C   s1   | |  _  d  |  _ d  |  _ | |  _ | |  _ d  S)N)iface_ephipv4leasedhcp_log_funcconnectivity_url)selfr   r   r   r   r   r   __init__)   s
    				zEphemeralDHCPv4.__init__c             C   s9   |  j  r/ t |  j   r/ t j d |  j   d S|  j   S)zUSetup sandboxed dhcp context, unless connectivity_url can already be
        reached.z:Skip ephemeral DHCP setup, instance has connectivity to %sN)r   r   LOGdebugobtain_lease)r   r   r   r   	__enter__0   s    	
zEphemeralDHCPv4.__enter__c             C   s   |  j    d S)z Teardown sandboxed dhcp context.N)clean_network)r   Z	excp_typeZ
excp_valueZexcp_tracebackr   r   r   __exit__;   s    zEphemeralDHCPv4.__exit__c             C   s9   |  j  r d |  _  |  j s d S|  j j d d d  d S)z@Exit _ephipv4 context to teardown of ip configuration performed.N)r   r   r    )r   r   r   r   r   ?   s
    			zEphemeralDHCPv4.clean_networkc             C   sb  |  j  r |  j  Sy t |  j |  j  } Wn1 t k
 r\ } z t   |  WYd d } ~ Xn X| sl t    | d |  _  t j d |  j  d |  j  d |  j  d  d d d d d d d	 d
 d d d g d d i } |  j |  } | d	 st	 | d | d  | d	 <| d r&t
 | d  | d <|  j r<|  j | d <t |   } | j   | |  _ |  j  S)a9  Perform 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.
        N   z#Received dhcp lease on %s for %s/%s	interfacezfixed-addresszsubnet-maskipZprefix_or_maskZ	broadcastzbroadcast-addressstatic_routeszrfc3442-classless-static-routeszclassless-static-routesZrouterZroutersr   )r   maybe_perform_dhcp_discoveryr   r   r   r   r   r   extract_dhcp_options_mappingbcipparse_static_routesr   r   r   r   )r   leasesenmapkwargsZephipv4r   r   r   r   G   s>    				

	
	zEphemeralDHCPv4.obtain_leasec             C   sb   i  } xU | j    D]G \ } } t | t  rD |  j | | |  q |  j j |  | | <q W| S)N)items
isinstancelistget_first_option_valuer   get)r   r,   resultZinternal_referencelease_option_namesr   r   r   r'   r   s    
z,EphemeralDHCPv4.extract_dhcp_options_mappingc             C   s:   x3 | D]+ } | j  |  s |  j j  |  | | <q Wd  S)N)r2   r   )r   Zinternal_mappingr4   r3   Zdifferent_namesr   r   r   r1      s    z&EphemeralDHCPv4.get_first_option_value)
r   r   r   r   r   r    r   r   r'   r1   r   r   r   r   r   (   s   +r   c             C   s   |  d k r5 t    }  |  d k rX t j d  g  Sn# |  t   k rX t j d |   g  St j d  } | s~ t j d  g  St j d d d d	 d
 d   } t | |  | |  SWd QRXd 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.dhclientz7Skip dhclient configuration: No dhclient command found.Zrmtree_ignore_errorsTprefixzcloud-init-dhcp-Z	needs_exe)	r   r   r   r   r	   Zwhichr   Ztempdirdhcp_discovery)Znicr   Zdhclient_pathZtdirr   r   r   r&      s"    	
r&   c             C   s   t  j d  } g  } t j |   } t |  d k rK t d j |     x | j |  D] } g  } x] | j d  D]L } | j	   j
 d d  j
 d d  } | s qw | j | j d d	   qw W| j t |   q[ W| s t d
 j |     | S)a  Parse the given dhcp lease file for the most recent lease.

    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};" zoption  r!   z1Cannot parse dhcp lease file {0}. No leases found)recompiler
   	load_filelenr   formatfindallsplitstripreplaceappenddict)
lease_fileZlease_regexZdhcp_leasesZlease_contentr   Zlease_optionsliner   r   r   parse_dhcp_lease_file   s&    	$rI   c             C   s   t  j d |  t j j | d  } t j |  |  t j j | d  } t j j | d  } t j | t j  sz |  } t	 j	 d d d d | d	 g d
 d | d d d | d | | d d g
 } t	 j	 | d
 d \ } }	 t j
 | | g d d d d }
 |
 r/t  j d d j d d   |
 D   g  Sd } d } x t d d  D] } t j |  j   } y t |  } Wn t k
 rYnF Xt j |  } | d k rt  j d |  t j | t j  d } Pt j d  qKW| st  j d  | | d"  | d! k	 r| | |	  t |  S)#a  Run dhclient on the interface without scripts or filesystem artifacts.

    @param dhclient_cmd_path: Full path to the dhclient used.
    @param interface: Name of the network inteface on which to dhclient.
    @param cleandir: The directory from which to run dhclient as well as store
        dhcp leases.
    @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
        dhcp.leases file or empty list.
    z!Performing a dhcp discovery on %sr5   zdhclient.pidzdhcp.leasesr#   linksetZdevZupZcaptureTz-1z-vz-lfz-pfz-sfz	/bin/trueZmaxwait   Znapleng{Gz?z+dhclient did not produce expected files: %sz, c             s   s!   |  ] } t  j j |  Vq d  S)N)ospathbasename).0fr   r   r   	<genexpr>   s    z!dhcp_discovery.<locals>.<genexpr>unknownFr   i  r!   zkilling dhclient with pid=%szCdhclient(pid=%s, parentpid=%s) failed to daemonize after %s secondsNg      $@)r   r   rM   rN   joinr
   copyaccessX_OKr	   Zwait_for_filesZwarningranger>   rC   int
ValueErrorZget_proc_ppidkillsignalSIGKILLtimeZsleeperrorrI   )Zdhclient_cmd_pathr"   Zcleandirr   Zsandbox_dhclient_cmdZpid_filerG   cmdouterrZmissingZppidZ
daemonized_Zpid_contentpidr   r   r   r7      sN    %		r7   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.Zlist_valuesF)rF   	configobjZ	ConfigObjr   )Zcontentr   r   r   networkd_parse_lease  s    rf   c             C   st   |  d k r t  }  i  } t j j |   s. | Sx? 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_DIRrM   rN   isdirlistdirrf   r
   r>   rT   )leases_dZretZlfiler   r   r   networkd_load_leases"  s    )rk   c             C   s_   | d  k r t  } t d |  } x7 t | j    D]# \ } } | j |   r4 | |  Sq4 Wd  S)Nrj   )rg   rk   sortedr.   r2   )Zkeynamerj   r*   Z_ifindexdatar   r   r   networkd_get_option_from_leases4  s    rn   c                s    j  d    d d   t j d    D } g  }   f d d   } d } xt |  D]\ } } | | k  rt qY t |  } | t d d	  k r*d
 } t | | d   | k  r | | | t | | d    | Sd j | | d | d   }	 d j | | d | |   }
 | | } n| t d d  k rd } t | | d   | k  r| | | t | | d    | Sd j | | d | d  d g  }	 d j | | d | |   }
 | | } n| t d
 d  k rd } t | | d   | k  r6| | | t | | d    | Sd j | | d | d  d d g  }	 d j | | d | |   }
 | | } nQ| t d d
  k rFd } t | | d   | k  r| | | t | | d    | Sd j | | d | d  d d d g  }	 d j | | d | |   }
 | | } n | d k rd } t | | d   | k  r| | | t | | d    | Sd }	 d j | | d | |   }
 | | } n t j	 d |  | S| j
 d |	 | f |
 f  qY 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
    r8   c             S   s   g  |  ] } | r |  q Sr   r   )rP   tokr   r   r   
<listcomp>Y  s   	 z'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   r_   )ZcidrZrequiredZremainmsg)rfc3442r   r   _trunc_error\  s    z)parse_static_routes.<locals>._trunc_errorr      !   	   N.r!   rL            0            z0.0.0.0zSParsed invalid net length "%s".  Verify DHCP rfc3442-classless-static-routes value.z%s/%s)rstripr<   rB   	enumeraterY   rX   r?   rT   r   r_   rE   )rr   tokensr$   rs   Zcurrent_idxidxro   Z
net_lengthZreq_toksZnet_addressZgatewayr   )rr   r   r)   >  sl     !! (! +! .! !	!r)   )$re   ZloggingrM   r<   r\   r^   ior   Zcloudinit.netr   r   r   r   Zcloudinit.net.network_stater   r(   Z	cloudinitr   r	   r
   Z	getLoggerr   r   rg   	Exceptionr   r   objectr   r&   rI   r7   rf   rk   rn   r)   r   r   r   r   <module>   s.   "^!Q
