o
    w7exD                  	   @   sb  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)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e0fd!d"Z1G d#d$ d$ej2Z3de4fd%d&Z5	'd>d(ej6d)e*d*e4fd+d,Z7	'd>d(ej6d)e*d*e4dej8fd-d.Z9G d/d0 d0eZ:G d1d2 d2Z;e'e:j<fd3e*d4e:de0fd5d6Z=e3ej>ffgZ?d7d8 Z@e$d9kr/ddlAZAd:ZBeAjCeBd;ZDeDE  eFeGe=e:j<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)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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 r3   A/usr/lib/python3/dist-packages/cloudinit/sources/DataSourceLXD.py_get_fallback_interface_name0   s(   
r5   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)r:   namesubnets)versionconfig)r   r*   debugr5   )r6   primary_nicr3   r3   r4   generate_network_configJ   s$   
rB   c                       $   e Zd Z fddZdd Z  ZS )SocketHTTPConnectionc                    s   t  d || _d S N	localhost)super__init__socket_pathselfrI   	__class__r3   r4   rH   q      
zSocketHTTPConnection.__init__c                 C   s$   t  t jt j| _| j| j d S N)socketAF_UNIXSOCK_STREAMsockconnectrI   rK   r3   r3   r4   rT   u   s   zSocketHTTPConnection.connect)__name__
__module____qualname__rH   rT   __classcell__r3   r3   rL   r4   rD   p       rD   c                       rC   )SocketConnectionPoolc                    s   || _ t d d S rE   )rI   rG   rH   rJ   rL   r3   r4   rH   {   s   zSocketConnectionPool.__init__c                 C   s
   t | jS rO   )rD   rI   rU   r3   r3   r4   	_new_conn   s   
zSocketConnectionPool._new_conn)rV   rW   rX   rH   r\   rY   r3   r3   rL   r4   r[   z   rZ   r[   c                   @   s   e Zd ZdddZdS )LXDSocketAdapterNc                 C   s   t tS rO   )r[   LXD_SOCKET_PATH)rK   urlproxiesr3   r3   r4   get_connection   s   zLXDSocketAdapter.get_connectionrO   )rV   rW   rX   ra   r3   r3   r3   r4   r]      s    r]   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)rb   metadata_valueparsed_metadataexcr3   r3   r4   _raw_instance_data_to_dict   s.   

rn   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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)rG   	_unpickleskip_hotplug_detect)rK   ru   rL   r3   r4   rv      rN   zDataSourceLXD._unpicklec                   C   s   t  S )z@Check platform environment to report if this datasource may run.)is_platform_viabler3   r3   r3   r4   	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(td|}t	| jt
s:t t | j|g| _d| jv rE| jd | _d| jv rStd| jd | _d| jv r^| 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?   rs   r   r   r   T)r   log_timer*   r@   read_metadatarr   rn   getmetadatare   rf   mergemanydictrg   userdata_rawrq   vendordata_raw)rK   r?   user_metadatar3   r3   r4   	_get_data   s6   



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}))verrP   )rj   LXD_SOCKET_API_VERSIONr^   rU   r3   r3   r4   _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   re   rf   r   rg   r   )rK   sys_cfgresponsemdr3   r3   r4   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 )r:   nicr3   ).0kvr3   r3   r4   
<listcomp>   s
    z0DataSourceLXD.network_config.<locals>.<listcomp>z8LXD datasource generating network config using fallback.)rq   r   UNSETrr   r   re   rf   r   r*   r@   itemsrB   r   )rK   r   r3   r3   r4   network_config   s(   

zDataSourceLXD.network_config)rV   rW   rX   dsnamer   r   rq   r
   r   str__annotations__rr   
DataSourcert   r	   rw   intrv   staticmethodboolry   r   r   r   propertyrf   r   rY   r3   r3   rL   r4   ro      s"   
 

ro   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_moder3   r3   r3   r4   rx     s   rx   Tsessionr_   do_raisec              
   C   sf   t | ||}|jstd||j|j i S z| W S  ty2 } zt	dj
||jd|d }~ww )NSkipping %s on [HTTP:%d]:%szFUnable to process LXD config at {url}. Expected JSON but found: {resp})r_   resp)_do_requestokr*   r@   status_codetextjsonr   r   ri   rj   )r   r_   r   url_responserm   r3   r3   r4   _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	|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})coderouter   )ranger   r   timesleepr*   r+   r@   r   r   ri   rj   r   )r   r_   r   retriesr   r3   r3   r4   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)	rV   rW   rX   r   NONECONFIGDEVICESr   ALLr3   r3   r3   r4   r   J  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 rO   )r   r   combine_urlLXD_URL_version_url)rK   r   r3   r3   r4   rH   S  s   z_MetaDataReader.__init__r   r   c           	   
   C   s   di i}t | jd}t||}t|D ]I}t t|}t||dd}|js1t	d||j
|j q|dd }|j|d |< |tv r]t| |vrQ|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-initr7   )r   r   r   r   sortedr   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cfg_keyr3   r3   r4   _process_configW  s>   

z_MetaDataReader._process_configr   c                C   s   t  Q}|| jt  d| ji}tj|v r&t	| jd}t
||j|d< tj|v r3|| | tj|v rLt	| jd}t||dd}|rL||d< |W  d    S 1 sXw   Y  d S )N_metadata_api_versionr}   r   Fr   )requestsSessionmountr   r]   r   r   r   r   r   r   r   r   updater   r   r   )rK   r   r   r   md_router_   r   r3   r3   r4   __call__  s"   




$z_MetaDataReader.__call__N)rV   rW   rX   r   r   rH   r   r   rf   r   r   r   r3   r3   r3   r4   r   R  s    7r   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   r3   r3   r4   r     s   #r   c                 C   s   t | tS rO   )r   list_from_dependsdatasources)dependsr3   r3   r4   get_datasource_list  s   r   __main__z*Query LXD metadata and emit a JSON object.)descriptionr   rO   )T)H__doc__r   rP   r   r   enumr   r   json.decoderr   typingr   r   r   r   r	   r
   r   r   requests.adaptersr   urllib3.connectionr   urllib3.connectionpoolr   	cloudinitr   r   loggingr   r   r   r   cloudinit.netr   	getLoggerrV   r*   r^   r   r   r   r   r5   rB   rD   r[   r]   rf   rn   r   ro   r   rx   r   r   Responser   r   r   r   r   DEP_FILESYSTEMr   r   argparser   ArgumentParserparser
parse_argsprint
json_dumpsr3   r3   r3   r4   <module>   s    
$




&
	j

Q

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