U
    ӇgD                     @   s:  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(e)dddZ*d9eee)  ee)ef dddZ+G dd deZ,G dd deZ-G dd deZ.e)e/ddd Z0G d!d" d"ej1Z2e3dd#d$Z4d:ej5e)e3d&d'd(Z6d;ej5e)e3ej7d)d*d+Z8G d,d- d-eZ9G d.d/ d/Z:e&e9j;fe)e9e/d0d1d2Z<e2ej=ffgZ>d3d4 Z?e#d5kr6ddl@Z@d6ZAe@jBeAd7ZCeCD  eEeFe<e9j;d8 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rzt  dg\}}W n8 t jk
rZ } ztd| |  W Y S d }~X Y nX | dkrt d d }|dkrdS |d	krd
S dS | S )NZeth0zsystemd-detect-virtzHUnable to run systemd-detect-virt: %s. Rendering default network config.)ZkvmZqemuuname   Zppc64leZenp0s5Zs390xZenc9Zenp5s0)r   ZwhichZProcessExecutionErrorLOGwarningstripr   Zsystem_info)Zdefault_nameZ	virt_type_errZarch r%   A/usr/lib/python3/dist-packages/cloudinit/sources/DataSourceLXD.py_get_fallback_interface_name/   s$    
r'   )nicsr   c                 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   ZphysicalZdhcpr   )typeZcontrol)r*   nameZsubnets)versionconfig)r   r    debugr'   )r(   Zprimary_nicr%   r%   r&   generate_network_configI   s$    
r/   c                       s$   e Zd Z fddZdd Z  ZS )SocketHTTPConnectionc                    s   t  d || _d | _d S NZ	localhost)super__init__socket_pathsockselfr4   	__class__r%   r&   r3   p   s    zSocketHTTPConnection.__init__c                 C   s$   t  t jt j| _| j| j d S N)socketZAF_UNIXZSOCK_STREAMr5   connectr4   r7   r%   r%   r&   r<   u   s    zSocketHTTPConnection.connect)__name__
__module____qualname__r3   r<   __classcell__r%   r%   r8   r&   r0   o   s   r0   c                       s$   e Zd Z fddZdd Z  ZS )SocketConnectionPoolc                    s   || _ t d d S r1   )r4   r2   r3   r6   r8   r%   r&   r3   {   s    zSocketConnectionPool.__init__c                 C   s
   t | jS r:   )r0   r4   r=   r%   r%   r&   	_new_conn   s    zSocketConnectionPool._new_conn)r>   r?   r@   r3   rC   rA   r%   r%   r8   r&   rB   z   s   rB   c                   @   s    e Zd ZdddZdddZdS )LXDSocketAdapterNc                 C   s   t tS r:   )rB   LXD_SOCKET_PATH)r7   urlproxiesr%   r%   r&   get_connection   s    zLXDSocketAdapter.get_connectionc                 C   s   |  |j|S r:   )rH   rF   )r7   ZrequestZverifyrG   Zcertr%   r%   r&   get_connection_with_tls_context   s    z0LXDSocketAdapter.get_connection_with_tls_context)N)NN)r>   r?   r@   rH   rI   r%   r%   r%   r&   rD      s   
   rD   )metadata_typer   c              
   C   s   t |tr|S |dkri S zt|}W n8 tk
r` } ztdj| |d|W 5 d}~X Y nX |dk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})Zmd_typevaluez:Invalid {md_type} format. Expected YAML but found: {value})
isinstancedictr   	load_yamlAttributeErrorr   InvalidMetaDataExceptionformat)rJ   Zmetadata_valueZparsed_metadataexcr%   r%   r&   _raw_instance_data_to_dict   s.    

  rS   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edd	 fd
dZeedddZedddZe	dddZe	dddZeedddZ  ZS )DataSourceLXDZLXD_network_config_crawled_metadata)user.meta-datar   r   r   r   .sensitive_metadata_keysTN)ci_pkl_versionr   c                    s   t  | d| _d S )NT)r2   	_unpickleskip_hotplug_detect)r7   rY   r8   r%   r&   rZ      s    zDataSourceLXD._unpickler   c                   C   s   t  S )z@Check platform environment to report if this datasource may run.)is_platform_viabler%   r%   r%   r&   	ds_detect   s    zDataSourceLXD.ds_detectc                 C   s   t  | _td| jd| _| jdi }|di }|rL| jtd| d| jkrb| jd | _d| jkr~td| jd | _d| jkr| jd | _dS )z=Crawl LXD socket API instance data and return True on success	meta-datar-   rW   r   r   r   T)	read_metadatarV   rS   getmetadataupdateZuserdata_rawrU   Zvendordata_raw)r7   r-   Zuser_metadatar%   r%   r&   	_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}))Zverr;   )rQ   LXD_SOCKET_API_VERSIONrE   r=   r%   r%   r&   _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`   rL   rM   r   rN   ra   )r7   Zsys_cfgresponsemdr%   r%   r&   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rPt	d | jd | _ n.| jdr~dd | jd 
 D }t|| _ | j tjkrt	d t | _ tt| j S )zNetwork 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 )r*   Znicr%   ).0kvr%   r%   r&   
<listcomp>   s   z0DataSourceLXD.network_config.<locals>.<listcomp>z8LXD datasource generating network config using fallback.)rU   r   UNSETrV   rc   rL   rM   r`   r    r.   itemsr/   r   )r7   rm   r%   r%   r&   network_config   s(    

zDataSourceLXD.network_config)r>   r?   r@   Zdsnamer   rr   rU   r
   r   str__annotations__rV   r   
DataSourcerX   r	   r[   intrZ   staticmethodboolr]   rc   re   rl   propertyrM   rt   rA   r%   r%   r8   r&   rT      s    
rT   c                   C   s"   t jtrtt tjS dS )z=Return True when this platform appears to have an LXD socket.F)ospathexistsrE   statS_ISSOCKlstatst_moder%   r%   r%   r&   r\     s    r\   T)sessionrF   do_raisec              
   C   s   t | ||}|js0td||j|jd i S z
| W S  tk
rz } z"t	
dj||jdd|W 5 d }~X Y nX d S )NSkipping %s on [HTTP:%d]:%sutf-8zFUnable to process LXD config at {url}. Expected JSON but found: {resp})rF   resp)_do_requestokr    r.   status_codecontentdecodeZjsonr   r   rP   rQ   )r   rF   r   Zurl_responserR   r%   r%   r&   _get_json_response  s(    

 
r   )r   rF   r   r   c                 C   s   t dddD ]:}| |}d|jkrBtd td|j|| q qHqtd|j| |r|jst	
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   )codeZrouter   )ranger`   r   timesleepr    r!   r.   r   r   rP   rQ   r   r   )r   rF   r   Zretriesrj   r%   r%   r&   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 )rh   N)	r>   r?   r@   r   ZNONECONFIGDEVICESri   ALLr%   r%   r%   r&   rh   J  s
   rh   c                   @   s@   e Zd ZefedddZejedddZ	e
eddd	Zd
S )_MetaDataReaderapi_versionc                 C   s   || _ tt| j | _d S r:   )r   r   combine_urlLXD_URL_version_url)r7   r   r%   r%   r&   r3   S  s    z_MetaDataReader.__init__)r   r   c           
   
   C   s   di i}t | jd}t||}t|D ]}t t|}t||dd}|jd}|j	slt
d||j| q(|dd }	||d |	< |	tkr(t|	 |kr||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-initr)   )r   r   r   r   sortedr   r   r   r   r   r    r.   r   
rpartitionCONFIG_KEY_ALIASESr!   replace)
r7   r   r-   Z
config_urlZconfig_routesZconfig_routeZconfig_route_urlZconfig_route_responseZresponse_textZcfg_keyr%   r%   r&   _process_configW  s<    

  z_MetaDataReader._process_config)rg   r   c             
   C   s   t  }|| jt  d| ji}tj|krRt	| jd}t
||jd|d< tj|krl|| | tj|krt	| jd}t||dd}|r||d< |W  5 Q R  S Q R X d S )NZ_metadata_api_versionr^   r   rm   Fr   )requestsSessionZmountr   rD   r   rh   ri   r   r   r   r   r   r   rb   r   r   r   )r7   rg   r   rk   Zmd_routerF   rm   r%   r%   r&   __call__  s,    


  

z_MetaDataReader.__call__N)r>   r?   r@   rd   ru   r3   r   r   rM   r   rh   r   r%   r%   r%   r&   r   R  s   6r   )r   rg   r   c                 C   s   t | d|dS )a8  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 `is_platform_viable` 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   rf   )r   )r   rg   r%   r%   r&   r_     s    #r_   c                 C   s   t | tS r:   )r   Zlist_from_dependsdatasources)Zdependsr%   r%   r&   get_datasource_list  s    r   __main__z*Query LXD metadata and emit a JSON object.)descriptionrf   )N)T)T)G__doc__Zloggingr|   r;   r   r   enumr   r   Zjson.decoderr   typingr   r   r   r   r	   r
   r   r   Zrequests.adaptersr   Zurllib3.connectionr   Zurllib3.connectionpoolr   Z	cloudinitr   r   r   r   r   Zcloudinit.netr   Z	getLoggerr>   r    rE   rd   r   r   ru   r'   r/   r0   rB   rD   rM   rS   rw   rT   rz   r\   r   r   ZResponser   rh   r   r   r_   ZDEP_FILESYSTEMr   r   argparser   ArgumentParserparser
parse_argsprintZ
json_dumpsr%   r%   r%   r&   <module>   s   
$

 

&	c      R*

