o
    fFE                  	   @   sV  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*	d;d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Z2de3fd#d$Z4	%d<d&ej5d'e)d(e3fd)d*Z6	%d<d&ej5d'e)d(e3dej7fd+d,Z8G d-d. d.eZ9G d/d0 d0Z:e&e9j;fd1e)d2e9de/fd3d4Z<e2ej=ffgZ>d5d6 Z?e#d7kr)ddl@Z@d8ZAe@jBeAd9ZCeCD  eEeFe<e9j;d: 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   s   t  S )z@Check platform environment to report if this datasource may run.)is_platform_viabler2   r2   r2   r3   	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 }|r,| j	td| d| jv r7| jd | _
d| jv rEtd| jd | _d| jv rP| jd | _d	S )
z=Crawl LXD socket API instance data and return True on successzCrawl of metadata service)logfuncmsgfunc	meta-datar>   rt   r   r   r   T)r   log_timer)   r?   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 )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 )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boolrz   r   r   r   propertyrg   r   rW   r2   r2   rL   r3   rp      s"   
 rp   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existsr[   statS_ISSOCKlstatst_moder2   r2   r2   r3   ry     s   ry   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   1  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   N  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   W  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_config[  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   V  s    6r   r   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   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)G__doc__loggingr   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   ry   r   r   Responser   r   r   r   r   DEP_FILESYSTEMr   r   argparser   ArgumentParserparser
parse_argsprint
json_dumpsr2   r2   r2   r3   <module>   s    
$




&	g

R

*
