U
    AdD                     @   s:  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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)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/d0 d0Z:e&e9j;fe)e9e/d1d2d3Z<e2ej=ffgZ>d4d5 Z?e#d6kr6ddl@Z@d7ZAe@jBeAd8ZCeCD  eEeFe<e9j;d9 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://linuxcontainers.org/lxd/docs/master/dev-lxd
    N)Flagauto)JSONDecodeError)AnyDictListOptionalTupleUnioncast)HTTPAdapter)HTTPConnection)HTTPConnectionPool)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_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 S NZ	localhost)super__init__socket_path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_STREAMZsockconnectr4   r6   r%   r%   r&   r;   t   s    zSocketHTTPConnection.connect)__name__
__module____qualname__r3   r;   __classcell__r%   r%   r7   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   r5   r7   r%   r&   r3   z   s    zSocketConnectionPool.__init__c                 C   s
   t | jS r9   )r0   r4   r<   r%   r%   r&   	_new_conn~   s    zSocketConnectionPool._new_conn)r=   r>   r?   r3   rB   r@   r%   r%   r7   r&   rA   y   s   rA   c                   @   s   e Zd ZdddZdS )LXDSocketAdapterNc                 C   s   t tS r9   )rA   LXD_SOCKET_PATH)r6   urlZproxiesr%   r%   r&   get_connection   s    zLXDSocketAdapter.get_connection)N)r=   r>   r?   rF   r%   r%   r%   r&   rC      s   rC   )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)rG   Zmetadata_valueZparsed_metadataexcr%   r%   r&   _raw_instance_data_to_dict   s.    

  rP   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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)r6   rV   r7   r%   r&   rW      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&   _is_platform_viable   s    z!DataSourceLXD._is_platform_viablec                 C   s   |   std dS tjtjdtd| _td| jd| _	| jdi }|di }|rftd|}t
| j	tst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+Not an LXD datasource: No LXD socket found.FzCrawl of metadata service)Zlogfuncmsgfunc	meta-datar-   rT   r   r   r   T)rZ   r    r.   r   Zlog_timeread_metadatarS   rP   getmetadatarI   rJ   ZmergemanydictrK   Zuserdata_rawrR   Zvendordata_raw)r6   r-   Zuser_metadatar%   r%   r&   	_get_data   sB    
 
 

 
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:   )rN   LXD_SOCKET_API_VERSIONrD   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_   rI   rJ   r   rK   r`   )r6   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.)rR   r   UNSETrS   ra   rI   rJ   r_   r    r.   itemsr/   r   )r6   rk   r%   r%   r&   network_config   s(    

zDataSourceLXD.network_config)r=   r>   r?   Zdsnamer   rp   rR   r
   r   str__annotations__rS   
DataSourcerU   r	   rX   intrW   boolrZ   ra   rc   rj   propertyrJ   rr   r@   r%   r%   r7   r&   rQ      s   


"rQ   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existsrD   statS_ISSOCKlstatst_moder%   r%   r%   r&   rY     s    rY   T)sessionrE   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})rE   resp)_do_requestokr    r.   status_codetextZjsonr   r   rM   rN   )r   rE   r   Zurl_responserO   r%   r%   r&   _get_json_response  s(    
 r   )r   rE   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   rM   rN   r   )r   rE   r   Zretriesrh   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 )rf   N)	r=   r>   r?   r   ZNONECONFIGDEVICESrg   ALLr%   r%   r%   r&   rf   L  s
   rf   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 r9   )r   r   combine_urlLXD_URL_version_url)r6   r   r%   r%   r&   r3   U  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)	r6   r   r-   Z
config_urlZconfig_routesZconfig_routeZconfig_route_urlZconfig_route_responseZcfg_keyr%   r%   r&   _process_configY  s@    

  z_MetaDataReader._process_config)re   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]   rk   Fr   )requestsSessionZmountr   rC   r   rf   rg   r   r   r   r   r   updater   r   r   )r6   re   r   ri   Zmd_routerE   rk   r%   r%   r&   __call__  s"    


 

z_MetaDataReader.__call__N)r=   r>   r?   rb   rs   r3   r   r   rJ   r   rf   r   r%   r%   r%   r&   r   T  s   7r   )r   re   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   rd   )r   )r   re   r%   r%   r&   r^     s    #r^   c                 C   s   t | tS r9   )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.)descriptionrd   )N)T)T)G__doc__ry   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   Zloggingr   r   r   r   Zcloudinit.netr   Z	getLoggerr=   r    rD   rb   r   r   rs   r'   r/   r0   rA   rC   rJ   rP   ru   rQ   rw   rY   r   r   ZResponser   rf   r   r   r^   ZDEP_FILESYSTEMr   r   argparser   ArgumentParserparser
parse_argsprintZ
json_dumpsr%   r%   r%   r&   <module>   s   
$

 

&
	m      Q*

