o
    x[höD  ã                	   @   sH  d 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mZ ddl	m
Z
 ddlmZmZmZmZmZmZmZ ddlZddlmZ ddlmZ ddlmZ dd	lmZmZmZmZmZ dd
l m!Z! e "e#¡Z$dZ%dZ&dZ'dddddddœZ(de)fdd„Z*	d9deee)  dee)ef fdd„Z+G dd„ deƒZ,G dd„ deƒZ-G dd„ deƒZ.de)de/fdd „Z0G d!d"„ d"ej1ƒZ2	#d:d$ej3d%e)d&e4fd'd(„Z5	#d:d$ej3d%e)d&e4dej6fd)d*„Z7G d+d,„ d,eƒZ8G d-d.„ d.ƒZ9e&e8j:fd/e)d0e8de/fd1d2„Z;e2ej<ffgZ=d3d4„ Z>e#d5k�r"ddl?Z?d6Z@e?jAe@d7�ZBeB C¡  eDe Ee;e8j:d8�¡ƒ dS dS );a•  Datasource for LXD, reads /dev/lxd/sock representation of instance data.

Notes:
 * This datasource replaces previous NoCloud datasource for LXD.
 * Older LXD images may not have updates for cloud-init so NoCloud may
   still be detected on those images.
 * Detect LXD datasource when /dev/lxd/sock is an active socket file.
 * Info on dev-lxd API: https://documentation.ubuntu.com/lxd/en/latest/dev-lxd/
é    N)ÚFlagÚauto)ÚJSONDecodeError)ÚAnyÚDictÚListÚOptionalÚTupleÚUnionÚcast)ÚHTTPAdapter)ÚHTTPConnection)ÚHTTPConnectionPool)Úatomic_helperÚsourcesÚsubpÚ
url_helperÚutil)Úfind_fallback_nicz/dev/lxd/sockz1.0z
http://lxdú	user-dataúnetwork-configúvendor-data)úcloud-init.user-datazcloud-init.network-configúcloud-init.vendor-dataúuser.user-datazuser.network-configúuser.vendor-dataÚreturnc               
   C   s–   d} t  d¡rIz
t   dg¡\}}W n t jy, } zt d|¡ | W  Y d }~S d }~ww | ¡ dv rIt ¡ d d }|dkrAdS |d	krGd
S dS | S )NÚeth0zsystemd-detect-virtzHUnable to run systemd-detect-virt: %s. Rendering default network config.)ÚkvmÚqemuÚunameé   Úppc64leÚenp0s5Ús390xÚenc9Úenp5s0)r   ÚwhichÚProcessExecutionErrorÚLOGÚwarningÚstripr   Úsystem_info)Údefault_nameÚ	virt_typeÚ_ÚerrÚarch© r2   úA/usr/lib/python3/dist-packages/cloudinit/sources/DataSourceLXD.pyÚ_get_fallback_interface_name/   s(   
ý€úr4   Únicsc                 C   sF   t ƒ }|rt d|¡ n	tƒ }t d|¡ dd|dddœgdœgd	œS )
zCReturn network config V1 dict representing instance network config.zCLXD datasource generating network from discovered active device: %szVLXD datasource generating network from systemd-detect-virt platform default device: %sé   ÚphysicalÚdhcpr   )ÚtypeÚcontrol)r9   ÚnameÚsubnets)ÚversionÚconfig)r   r)   Údebugr4   )r5   Úprimary_nicr2   r2   r3   Úgenerate_network_configI   s$   ýý
ýÿþrA   c                       ó$   e Zd Z‡ fdd„Zdd„ Z‡  ZS )ÚSocketHTTPConnectionc                    s   t ƒ  d¡ || _d | _d S ©NÚ	localhost)ÚsuperÚ__init__Úsocket_pathÚsock©ÚselfrH   ©Ú	__class__r2   r3   rG   p   s   
zSocketHTTPConnection.__init__c                 C   s$   t   t jt j¡| _| j | j¡ d S ©N)ÚsocketÚAF_UNIXÚSOCK_STREAMrI   ÚconnectrH   ©rK   r2   r2   r3   rR   u   s   zSocketHTTPConnection.connect)Ú__name__Ú
__module__Ú__qualname__rG   rR   Ú__classcell__r2   r2   rL   r3   rC   o   s    rC   c                       rB   )ÚSocketConnectionPoolc                    s   || _ tƒ  d¡ d S rD   )rH   rF   rG   rJ   rL   r2   r3   rG   {   s   zSocketConnectionPool.__init__c                 C   s
   t | jƒS rN   )rC   rH   rS   r2   r2   r3   Ú	_new_conn   s   
zSocketConnectionPool._new_conn)rT   rU   rV   rG   rY   rW   r2   r2   rL   r3   rX   z   s    rX   c                   @   s"   e Zd Zddd„Z	ddd„ZdS )ÚLXDSocketAdapterNc                 C   s   t tƒS rN   )rX   ÚLXD_SOCKET_PATH)rK   ÚurlÚproxiesr2   r2   r3   Úget_connection„   s   zLXDSocketAdapter.get_connectionc                 C   s   |   |j|¡S rN   )r^   r\   )rK   ÚrequestÚverifyr]   Úcertr2   r2   r3   Úget_connection_with_tls_context‰   s   z0LXDSocketAdapter.get_connection_with_tls_contextrN   )NN)rT   rU   rV   r^   rb   r2   r2   r2   r3   rZ   ƒ   s    
ÿrZ   Úmetadata_typec              
   C   sx   t |tƒr|S |du ri S zt |¡}W n ty+ } zt dj| |d�¡|‚d}~ww |du r:t dj| |d�¡‚|S )a6  Convert raw instance data from str, bytes, YAML to dict

    :param metadata_type: string, one of as: meta-data, vendor-data, user-data
        network-config

    :param metadata_value: str, bytes or dict representing or instance-data.

    :raises: InvalidMetaDataError on invalid instance-data content.
    NzAInvalid {md_type}. Expected str, bytes or dict but found: {value})Úmd_typeÚvaluez:Invalid {md_type} format. Expected YAML but found: {value})Ú
isinstanceÚdictr   Ú	load_yamlÚAttributeErrorr   ÚInvalidMetaDataExceptionÚformat)rc   Úmetadata_valueÚparsed_metadataÚexcr2   r2   r3   Ú_raw_instance_data_to_dict�   s.   

ÿÿý€ÿÿÿro   c                       sÊ   e Zd ZU dZejZeee	f e
d< ejZeeee	f  e
d< ejjd Zee	df e
d< dZded	d
f‡ fdd„Zed	efdd„ƒZd	efdd„Zd	e	fdd„Zd	e	fdd„Zed	efdd„ƒZ‡  ZS )ÚDataSourceLXDÚLXDÚ_network_configÚ_crawled_metadata)úuser.meta-datar   r   r   r   .Úsensitive_metadata_keysTÚci_pkl_versionr   Nc                    s   t ƒ  |¡ d| _d S )NT)rF   Ú	_unpickleÚskip_hotplug_detect)rK   rv   rL   r2   r3   rw   À   s   
zDataSourceLXD._unpicklec                   C   sB   t j t¡st dt¡ dS t t  t¡j	¡st dt¡ dS dS )z@Check platform environment to report if this datasource may run.z%s does not exist.Fz%s is not a socketT)
ÚosÚpathÚexistsr[   r)   r*   ÚstatÚS_ISSOCKÚlstatÚst_moder2   r2   r2   r3   Ú	ds_detectÄ   s   zDataSourceLXD.ds_detectc                 C   s˜   t ƒ | _td| j d¡ƒ| _| j di ¡}| di ¡}|r&| j td|ƒ¡ d| jv r1| jd | _d| jv r?td| jd ƒ| _d| jv rJ| jd | _dS )z=Crawl LXD socket API instance data and return True on successú	meta-datar>   rt   r   r   r   T)	Úread_metadatars   ro   ÚgetÚmetadataÚupdateÚuserdata_rawrr   Úvendordata_raw)rK   r>   Úuser_metadatar2   r2   r3   Ú	_get_dataÏ   s&   ÿÿ


ÿ
zDataSourceLXD._get_datac                 C   s   dj ttd�S )z.Return subplatform details for this datasourcez"LXD socket API v. {ver} ({socket}))ÚverrO   )rk   ÚLXD_SOCKET_API_VERSIONr[   rS   r2   r2   r3   Ú_get_subplatformå   s   ÿzDataSourceLXD._get_subplatformc                 C   sB   t tjd�}| di ¡}t|tƒst |¡}| d¡| j d¡kS )z%Return True if instance_id unchanged.©Úmetadata_keysr�   zinstance-id)	r‚   ÚMetaDataKeysÚ	META_DATArƒ   rf   rg   r   rh   r„   )rK   Úsys_cfgÚresponseÚmdr2   r2   r3   Úcheck_instance_idë   s
   

zDataSourceLXD.check_instance_idc                 C   s¨   | j tjkr?| jtjkr|  ¡  t| jtƒr?| j d¡r(t 	d¡ | jd | _ n| j d¡r?dd„ | jd  
¡ D ƒ}t|ƒ| _ | j tjkrNt 	d¡ tƒ | _ tt| j ƒS )zŽNetwork config read from LXD socket config/user.network-config.

        If none is present, then we generate fallback configuration.
        r   z,LXD datasource using provided network configÚdevicesc                 S   s    g | ]\}}|d  dkr|‘qS )r9   Únicr2   )Ú.0ÚkÚvr2   r2   r3   Ú
<listcomp>  s
    þz0DataSourceLXD.network_config.<locals>.<listcomp>z8LXD datasource generating network config using fallback.)rr   r   ÚUNSETrs   r‰   rf   rg   rƒ   r)   r?   ÚitemsrA   r   )rK   r•   r2   r2   r3   Únetwork_configó   s(   
ÿþ
ÿzDataSourceLXD.network_config)rT   rU   rV   Údsnamer   r›   rr   r
   r   ÚstrÚ__annotations__rs   r   Ú
DataSourceru   r	   rx   Úintrw   ÚstaticmethodÚboolr€   r‰   rŒ   r”   Úpropertyrg   r�   rW   r2   r2   rL   r3   rp   ¬   s"   
 ÿÿ
rp   TÚsessionr\   Údo_raisec              
   C   sr   t | ||ƒ}|jst d||j|j d¡¡ i S z| ¡ W S  ty8 } zt	 
dj||j d¡d�¡|‚d }~ww )NúSkipping %s on [HTTP:%d]:%súutf-8zFUnable to process LXD config at {url}. Expected JSON but found: {resp})r\   Úresp)Ú_do_requestÚokr)   r?   Ústatus_codeÚcontentÚdecodeÚjsonr   r   rj   rk   )r¦   r\   r§   Úurl_responsern   r2   r2   r3   Ú_get_json_response  s,   
ü
þÿû€ÿr²   c                 C   s‚   t dddƒD ]}|  |¡}d|jkr!t d¡ t d|j||¡ q t d|j|¡ |r?|js?t	 
dj|j||j d	¡d
�¡‚|S )Né   r   éÿÿÿÿiô  gš™™™™™¹?z,[GET] [HTTP:%d] %s, retrying %d more time(s)z[GET] [HTTP:%d] %sz3Invalid HTTP response [{code}] from {route}: {resp}r©   )ÚcodeÚrouterª   )Úrangerƒ   r­   ÚtimeÚsleepr)   r*   r?   r¬   r   rj   rk   r®   r¯   )r¦   r\   r§   Úretriesr’   r2   r2   r3   r«   ,  s*   


ü

ýÿr«   c                   @   s0   e Zd Zeƒ Zeƒ Zeƒ Zeƒ ZeeB eB ZdS )r�   N)	rT   rU   rV   r   ÚNONEÚCONFIGÚDEVICESr�   ÚALLr2   r2   r2   r3   r�   I  s    r�   c                   @   sD   e Zd Zefdefdd„Zdejdefdd„Z	de
defd	d
„ZdS )Ú_MetaDataReaderÚapi_versionc                 C   s   || _ t t| j ¡| _d S rN   )rÀ   r   Úcombine_urlÚLXD_URLÚ_version_url)rK   rÀ   r2   r2   r3   rG   R  s   z_MetaDataReader.__init__r¦   r   c           
   
   C   sÆ   di i}t  | jd¡}t||ƒ}t|ƒD ]L}t  t|¡}t||dd�}|j d¡}|j	s6t
 d||j|¡ q| d¡d }	||d |	< |	tv r`t|	 |vrT||t|	 < qt
 d|	|	 d	d
d¡¡ q|S )a’  Iterate on LXD API config items. Promoting CONFIG_KEY_ALIASES

        Any CONFIG_KEY_ALIASES which affect cloud-init behavior are promoted
        as top-level configuration keys: user-data, network-data, vendor-data.

        LXD's cloud-init.* config keys override any user.* config keys.
        Log debug messages if any user.* keys are overridden by the related
        cloud-init.* key.
        r>   F©r§   r©   r¨   ú/r´   z,Ignoring LXD config %s in favor of %s value.Úuserz
cloud-initr6   )r   rÁ   rÃ   r²   ÚsortedrÂ   r«   r®   r¯   r¬   r)   r?   r­   Ú
rpartitionÚCONFIG_KEY_ALIASESr*   Úreplace)
rK   r¦   r>   Ú
config_urlÚconfig_routesÚconfig_routeÚconfig_route_urlÚconfig_route_responseÚresponse_textÚcfg_keyr2   r2   r3   Ú_process_configV  s:   

ÿüý€z_MetaDataReader._process_configrŽ   c                C   sÄ   t  ¡ �T}| | jtƒ ¡ d| ji}tj|v r)t 	| jd¡}t
||ƒj d¡|d< tj|v r6| |  |¡¡ tj|v rOt 	| jd¡}t||dd�}|rO||d< |W  d   ƒ S 1 s[w   Y  d S )NÚ_metadata_api_versionr�   r©   r•   FrÄ   )ÚrequestsÚSessionÚmountrÃ   rZ   rÀ   r�   r�   r   rÁ   r«   r®   r¯   r¼   r…   rÒ   r½   r²   )rK   rŽ   r¦   r“   Úmd_router\   r•   r2   r2   r3   Ú__call__Œ  s*   


ÿÿþ

$îz_MetaDataReader.__call__N)rT   rU   rV   r‹   rŸ   rG   rÔ   rÕ   rg   rÒ   r�   rØ   r2   r2   r2   r3   r¿   Q  s    6r¿   rÀ   rŽ   c                 C   s   t | d�|d�S )a/  Fetch metadata from the /dev/lxd/socket routes.

    Perform a number of HTTP GETs on known routes on the devlxd socket API.
    Minimally all containers must respond to <LXD_SOCKET_API_VERSION>/meta-data
    when the LXD configuration setting `security.devlxd` is true.

    When `security.devlxd` is false, no /dev/lxd/socket file exists. This
    datasource will return False from `ds_detect` in that case.

    Perform a GET of <LXD_SOCKET_API_VERSION>/config` and walk all `user.*`
    configuration keys, storing all keys and values under a dict key
        LXD_SOCKET_API_VERSION: config {...}.

    In the presence of the following optional user config keys,
    create top level aliases:
      - user.user-data -> user-data
      - user.vendor-data -> vendor-data
      - user.network-config -> network-config

    :param api_version:
        LXD API version to operated with.
    :param metadata_keys:
        Instance of `MetaDataKeys` indicating what keys to fetch.
    :return:
        A dict with the following optional keys: meta-data, user-data,
        vendor-data, network-config, network_mode, devices.

        Below <LXD_SOCKET_API_VERSION> is a dict representation of all raw
        configuration keys and values provided to the container surfaced by
        the socket under the /1.0/config/ route.
    )rÀ   r�   )r¿   )rÀ   rŽ   r2   r2   r3   r‚   ¢  s   #ÿr‚   c                 C   s   t  | t¡S rN   )r   Úlist_from_dependsÚdatasources)Údependsr2   r2   r3   Úget_datasource_listÑ  s   rÜ   Ú__main__z*Query LXD metadata and emit a JSON object.)Údescriptionr�   rN   )T)FÚ__doc__Úloggingry   rO   r|   r¸   Úenumr   r   Újson.decoderr   Útypingr   r   r   r   r	   r
   r   rÔ   Úrequests.adaptersr   Úurllib3.connectionr   Úurllib3.connectionpoolr   Ú	cloudinitr   r   r   r   r   Úcloudinit.netr   Ú	getLoggerrT   r)   r[   r‹   rÂ   rÉ   rŸ   r4   rA   rC   rX   rZ   rg   ro   r¡   rp   rÕ   r¤   r²   ÚResponser«   r�   r¿   r¾   r‚   ÚDEP_FILESYSTEMrÚ   rÜ   ÚargparserÞ   ÚArgumentParserÚparserÚ
parse_argsÚprintÚ
json_dumpsr2   r2   r2   r3   Ú<module>   sž    
$
ú
ÿ
ÿ

þ&	jÿÿÿ
ÿÿÿÿÿ
þRþÿþ
ý
*ÿ
ÿú