U
    LdxD                     @   sF  d 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 dd
lmZ ddl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+d;e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*e0d d!d"Z1G d#d$ d$ej2Z3e4dd%d&Z5d<ej6e*e4d(d)d*Z7d=ej6e*e4ej8d+d,d-Z9G d.d/ d/eZ:G d0d1 d1Z;e'e:j<fe*e:e0d2d3d4Z=e3ej>ffgZ?d5d6 Z@e$d7krBddlAZAd8ZBeAjCeBd9ZDeDE  eFeGe=e:j<d: 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)log)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_name0   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_configJ   s$    
r0   c                       s$   e Zd Z fddZdd Z  ZS )SocketHTTPConnectionc                    s   t  d || _d S NZ	localhost)super__init__socket_pathselfr5   	__class__r&   r'   r4   q   s    zSocketHTTPConnection.__init__c                 C   s$   t  t jt j| _| j| j d S N)socketZAF_UNIXZSOCK_STREAMZsockconnectr5   r7   r&   r&   r'   r<   u   s    zSocketHTTPConnection.connect)__name__
__module____qualname__r4   r<   __classcell__r&   r&   r8   r'   r1   p   s   r1   c                       s$   e Zd Z fddZdd Z  ZS )SocketConnectionPoolc                    s   || _ t d d S r2   )r5   r3   r4   r6   r8   r&   r'   r4   {   s    zSocketConnectionPool.__init__c                 C   s
   t | jS r:   )r1   r5   r=   r&   r&   r'   	_new_conn   s    zSocketConnectionPool._new_conn)r>   r?   r@   r4   rC   rA   r&   r&   r8   r'   rB   z   s   rB   c                   @   s   e Zd ZdddZdS )LXDSocketAdapterNc                 C   s   t tS r:   )rB   LXD_SOCKET_PATH)r7   urlZproxiesr&   r&   r'   get_connection   s    zLXDSocketAdapter.get_connection)N)r>   r?   r@   rG   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)rH   Zmetadata_valueZparsed_metadataexcr&   r&   r'   _raw_instance_data_to_dict   s.    

  rQ   c                       s   e Zd ZU dZejZeee	f e
d< ejZ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)r3   	_unpickleskip_hotplug_detect)r7   rW   r8   r&   r'   rX      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 jtjdtd| _td| jd| _| jdi }|di }|rPtd|}t	| jt
stt t | j|g| _d| jkr| jd | _d| jkrtd| jd | _d| jkr| jd | _d	S )
z=Crawl LXD socket API instance data and return True on successzCrawl of metadata service)Zlogfuncmsgfunc	meta-datar.   rU   r   r   r   T)r   Zlog_timer!   r/   read_metadatarT   rQ   getmetadatarJ   rK   ZmergemanydictrL   Zuserdata_rawrS   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;   )rO   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`   rJ   rK   r   rL   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.)rS   r   UNSETrT   rb   rJ   rK   r`   r!   r/   itemsr0   r   )r7   rl   r&   r&   r'   network_config   s(    

zDataSourceLXD.network_config)r>   r?   r@   Zdsnamer   rq   rS   r
   r   str__annotations__rT   
DataSourcerV   r	   rY   intrX   staticmethodboolr[   rb   rd   rk   propertyrK   rs   rA   r&   r&   r8   r'   rR      s    


rR   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'   rZ     s    rZ   T)sessionrF   do_raisec              
   C   st   t | ||}|js*td||j|j i S z
| W S  tk
rn } zt	dj
||jd|W 5 d }~X Y nX d S )NSkipping %s on [HTTP:%d]:%szFUnable to process LXD config at {url}. Expected JSON but found: {resp})rF   resp)_do_requestokr!   r/   status_codetextZjsonr   r   rN   rO   )r   rF   r   Zurl_responserP   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s|t	
dj|j||j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})codeZrouter   )ranger`   r   timesleepr!   r"   r/   r   r   rN   rO   r   )r   rF   r   Zretriesri   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 )rg   N)	r>   r?   r@   r   ZNONECONFIGDEVICESrh   ALLr&   r&   r&   r'   rg   J  s
   rg   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'   r4   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sbt	d||j
|j q(|dd }|j|d |< |tkr(t| |kr|j|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   z,Ignoring LXD config %s in favor of %s value.userz
cloud-initr*   )r   r   r   r   sortedr   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cfg_keyr&   r&   r'   _process_configW  s@    

  z_MetaDataReader._process_config)rf   r   c             
   C   s   t  }|| jt  d| ji}tj|krLt	| jd}t
||j|d< tj|krf|| | 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^   rl   Fr   )requestsSessionZmountr   rD   r   rg   rh   r   r   r   r   r   updater   r   r   )r7   rf   r   rj   Zmd_routerF   rl   r&   r&   r'   __call__  s"    


 

z_MetaDataReader.__call__N)r>   r?   r@   rc   rt   r4   r   r   rK   r   rg   r   r&   r&   r&   r'   r   R  s   7r   )r   rf   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   re   )r   )r   rf   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.)descriptionre   )N)T)T)H__doc__r{   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   Zloggingr   r   r   r   Zcloudinit.netr   Z	getLoggerr>   r!   rE   rc   r   r   rt   r(   r0   r1   rB   rD   rK   rQ   rv   rR   ry   rZ   r   r   ZResponser   rg   r   r   r_   ZDEP_FILESYSTEMr   r   argparser   ArgumentParserparser
parse_argsprintZ
json_dumpsr&   r&   r&   r'   <module>   s   
$

 

&
	j      Q*

