o
    fR                     @   s,  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 d dlmZm	Z	m
Z
 d dlmZmZmZmZ d dlmZmZmZmZmZmZ d dlmZmZ dZdZeeZd	d
 Zde de defddZ!dd Z"d"ddZ#de$de%fddZ&de$de$de%fddZ'de$de$fddZ(G dd dej)Z)d"d d!Z*dS )#    N)SpooledTemporaryFile)CallableListOptional)featuressafeyamlsubputil)IPV6_DYNAMIC_TYPESSYS_CLASS_NETget_devicelistrenderershould_add_gateway_onlink_flagsubnet_is_ipv6)NET_CONFIG_TO_V2NetworkStatez/etc/netplan/50-cloud-init.yamls4  # This is the initial network config.
# It can be overwritten by cloud-init or console-conf.
network:
    version: 2
    ethernets:
        all-en:
            match:
                name: "en*"
            dhcp4: true
        all-eth:
            match:
                name: "eth*"
            dhcp4: true
c                    s   t  fdd|  D S )Nc                 3   s&    | ]\}}|  r||fV  qd S N)
startswith).0keyvaluematch 7/usr/lib/python3/dist-packages/cloudinit/net/netplan.py	<genexpr>,   s    
z,_get_params_dict_by_match.<locals>.<genexpr>)dictitems)configr   r   r   r   _get_params_dict_by_match+   s   r   r   entryr   c              	   C   s  d$dd}g }g }g }g }|  dg }	|	du rg }	|	D ]}
|
 d}|dr7|dkr/|d7 }||d	i q|tv rC|d
d	i q|dv rd|
 d }d|
v r[|d|
 d 7 }|
 dr|
 ddd}t|
d |r|td|
d | d	|d< || d|
v r|||
 dg 7 }d|
v r|||
 dg 7 }d|
v rd}t|
rd| v rd}|||
 di |
 dg D ]*}d| d| df }| d|d}d|v r|d| ddi || q|| qd| v r| d}|r| d |krt	d|| d | n| d |d< t
|dkr"|d |i t
|dkr0|d|i t
|dkrBd |i}|d!|i t
|dkr]| d!i }|d"|i |d!|i d#| v rx| d# durz|d#t|  d#i dS dS dS )%a  This method parse a cloudinit.net.network_state dictionary (config) and
       maps netstate keys/values into a dictionary (entry) to represent
       netplan yaml. (config v1 -> netplan)

    An example config dictionary might look like:

    {'mac_address': '52:54:00:12:34:00',
     'name': 'interface0',
     'subnets': [
        {'address': '192.168.1.2/24',
         'mtu': 1501,
         'type': 'static'},
        {'address': '2001:4800:78ff:1b:be76:4eff:fe06:1000",
         'mtu': 1480,
         'netmask': 64,
         'type': 'static'}],
      'type: physical',
      'accept-ra': 'true'
    }

    An entry dictionary looks like:

    {'set-name': 'interface0',
     'match': {'macaddress': '52:54:00:12:34:00'},
     'mtu': 1501}

    After modification returns

    {'set-name': 'interface0',
     'match': {'macaddress': '52:54:00:12:34:00'},
     'mtu': 1501,
     'address': ['192.168.1.2/24', '2001:4800:78ff:1b:be76:4eff:fe06:1000"],
     'ipv6-mtu': 1480}

     c                 S   s*   | rt | ts	| S || v r| |S | gS )zT
        Helper to convert strings to list of strings, handle single string
        )
isinstancestrsplit)objtokenr   r   r   _listifyX   s   
z$_extract_addresses.<locals>._listifysubnetsNtypedhcp4Tdhcp6)staticstatic6z%saddressprefixz/%dgatewaydefault)viatozAGateway %s is not contained within subnet %s, adding on-link flagzon-linkdns_nameservers
dns_searchmtuzipv6-mturoutesz%s/%snetworkmetricd   zZNetwork config: ignoring %s device-level mtu:%s because ipv4 subnet-level mtu:%s provided.r   	addressesnameserverssearchz	accept-ra)r!   )getr   updater
   r   LOGdebugappendr   warninglenr	   is_true)r   r    ifnamer   r'   r<   r8   r=   searchdomainsr(   subnetsn_typeaddr	new_routemtukeyrouteto_net	entry_mtunsr   r   r   _extract_addresses3   s   
%






rR   c                    s<   t  fdd|  D }t|dkr|d|i d S d S )Nc                    s$   g | ]\}}| d d kr|qS )zbond-masterN)r?   )r   namecfgbond_masterr   r   
<listcomp>   s
    z0_extract_bond_slaves_by_name.<locals>.<listcomp>r   
interfaces)sortedr   rE   r@   )rX   r    rV   bond_slave_namesr   rU   r   _extract_bond_slaves_by_name   s   
r[   c                    s~   t  d}tj|sd S t|}|tkrd S  fdddD }dd |D }t	d|| |g| D ]}t
| q5d S )Nz etc/netplan/00-snapd-config.yamlc                    s   g | ]}t  |qS r   )r   target_pathr   ftargetr   r   rW      s    
z"_clean_default.<locals>.<listcomp>)z-run/systemd/network/10-netplan-all-en.networkz.run/systemd/network/10-netplan-all-eth.networkz#run/systemd/generator/netplan.stampc                 S   s   g | ]
}t j|r|qS r   )ospathisfiler]   r   r   r   rW      s    z9removing known config '%s' and derived existing files: %s)r   r\   ra   rb   rc   r	   load_binary_fileKNOWN_SNAPD_CONFIGrA   rB   unlink)r`   tpathcontentderivedexistingr^   r   r_   r   _clean_default   s$   

rk   net_config_contentreturnc              
   C   s  zddl m} ddlm} W n ty   tdt Y dS w z@tdd1}|	|  |
  |dtj | }|| | }|| |tjt W d   n1 sYw   Y  W n tyy } ztd	t| W Y d}~dS d}~ww td
 dS )ad  Use netplan.State._write_yaml_file to write netplan config

    Where netplan python API exists, prefer to use of the private
    _write_yaml_file to ensure proper permissions and file locations
    are chosen by the netplan python bindings in the environment.

    By calling the netplan API, allow netplan versions to change behavior
    related to file permissions and treatment of sensitive configuration
    under the API call to _write_yaml_file.

    In future netplan releases, security-sensitive config may be written to
    separate file or directory paths than world-readable configuration parts.
    r   )Parser)Statez.No netplan python module. Fallback to write %sFwmodeNzUUnable to render network config using netplan python module. Fallback to write %s. %sz0Rendered netplan config using netplan python APIT)netplan.parserrn   netplan.statero   ImportErrorrA   rB   CLOUDINIT_NETPLAN_FILEr   writeflushseekioSEEK_SET	load_yamlimport_parser_results_write_yaml_filera   rb   basename	ExceptionrD   )rl   rn   ro   r^   parserstate_output_fileer   r   r   netplan_api_write_yaml_file   sF   




r   cfg_filerh   c                 C   s.   t j| sdS tt| }|t|kS )z<Return True when new netplan config has changed vs previous.T)ra   rb   existsr	   r|   load_text_file)r   rh   	prior_cfgr   r   r   has_netplan_config_changed  s   r   c                 C   sH   t jrdnd}tj| rt| }||@ |kr|}tj| ||d dS )zDWrite netplan config to cfg_file because python API was unavailable.i  i  rq   N)r   NETPLAN_CONFIG_ROOT_READ_ONLYra   rb   r   r	   get_permissions
write_file)r   rh   rr   current_moder   r   r   fallback_write_netplan_yaml   s   
r   c                   @   s   e Zd ZdZddgZddgZdddZdee fd	d
Z			dde
dee ddfddZdedefddZdddZde
defddZdS )RendererzBRenders network information in a /etc/netplan/network.yaml format.netplangenerateinfoNc                 C   sT   |si }| dt| _| dd | _| dd| _| dd| _| dp&g | _d S )Nnetplan_pathnetplan_headerpostcmdsFclean_defaultTr   )r?   rv   r   r   	_postcmdsr   	_features)selfr   r   r   r   __init__1  s   zRenderer.__init__rm   c              
   C   s   | j sEztj| jdd\}}t|}|d d | _ W | j S  tjy)   Y | j S  ttfyD } zt	d| W Y d }~| j S d }~ww | j S )NTcapturez
netplan.ior   z-Failed to list features from netplan info: %s)
r   r   NETPLAN_INFOr	   r|   ProcessExecutionError	TypeErrorKeyErrorrA   rB   )r   	info_blob_errr   r   r   r   r   r   :  s   
zRenderer.featuresnetwork_state	templatesc                 C   s   t jt|| j}tt j| | 	|}| j
r| j
nd}|ds*|d7 }|| }t||}t|s<t|| | jrDt|d | j| j|d | j| jd d S )N 
r_   )runconfig_changed)r   )ra   rb   joinr   r\   r   r	   
ensure_dirdirname_render_contentr   endswithr   r   r   r   rk   _netplan_generater   _net_setup_link)r   r   r   r`   fpnplanrh   headernetplan_config_changedr   r   r   render_network_stateH  s    	




zRenderer.render_network_stater   r   c                 C   s8   |s	t d d S |st d d S tj| jdd d S )Nz"netplan generate postcmds disabledzEskipping call to `netplan generate`. reason: identical netplan configTr   )rA   rB   r   NETPLAN_GENERATE)r   r   r   r   r   r   r   i  s   
zRenderer._netplan_generateFc                 C   s   |s	t d dS dt v rt d dS g d}t}tdD ]5}zt D ]}tj	t
| r<tj|t
| g dd q&W  dS  tjyU } z|}W Y d}~q d}~ww td	|)
zTo ensure device link properties are applied, we poke
        udev to re-evaluate networkd .link files and call
        the setup_link udev builtin command
        z'netplan net_setup_link postcmd disabledNznet.ifnames=0z%Predictable interface names disabled.)udevadmztest-builtinnet_setup_link   Tr   zQ'udevadm test-builtin net_setup_link' unable to run successfully for all devices.)rA   rB   r	   get_cmdliner   ranger   ra   rb   islinkr   r   r   RuntimeError)r   r   	setup_lnklast_exception_ifacer   r   r   r   r   u  s4   


zRenderer._net_setup_linkc           +   	   C   sF  |j dkrtd tjd|jidddS i }i }i }i }i }g }|jdg }|j}	|j	}
|
 D ]}|d}ttdd	 | }|d
}|dkr||dd d}|d d u rv|dd }|d urpd| i|d< n|d= |d= t|||| j |||i q2|dkri }i }td }dD ]%}t||}| D ]\}}||dd}|d u rq|||i qqt|dkr|d|i |dr|d  |d< |d}|dkrt||| t|||| j |||i q2|dkr|d}|d u rtdd| d q2tt|}d|i}d}t||}i }td } | D ]8\}}| |}|d u r9q)|||i |dv r`i }!|D ]}"|" \}#}$t|$|!|#< qI|||!i q)t|dkrp|d|i |dr~|d  |d< t|||| j |||i q2|d kr|d!|d"d#}%|dd }|d ur| |%d< t||%|| j |||%i q2|	s|
r|	|
d$}&|||||fD ] }'|' D ]\}(})d%|)v sd&|)vrq|)d%|&i qאqd'd( }*|d) ||*d*|7 }||*d+|7 }||*d,|7 }||*d-|7 }||*d.|7 }d/|S )0N   zV2 to V2 passthroughr9   F)explicit_startexplicit_endrX   rS   c                 S   s   | d d uS )N   r   )itr   r   r   <lambda>  s    z*Renderer._render_content.<locals>.<lambda>r)   physicalr   )set-namer   mac_address
macaddressr   bond)bond_zbond-r   -r   
parameterszbond-slavesnonebridgebridge_portszInvalid config. The keyz'bridge_ports' is required in .bridge_)z	path-costzport-priorityvlanvlan_idzvlan-raw-device)idlink)r<   r>   r=   r<   c                 S   s0   |rt j| |idddd}t|d}|gS g S )NFT)r   r   noaliasz    )r   dumpstextwrapindent)rS   sectiondumptxtr   r   r   _render_section!  s   z1Renderer._render_content.<locals>._render_sectionznetwork:
    version: 2
	ethernetswifisbondsbridgesvlansr   )versionrA   rB   r   r   r   _network_stater?   r5   dns_searchdomainsiter_interfacesr   filterr   lowerrR   r   r@   r   r   replacerE   r[   rD   rY   copyr$   intrC   r   )+r   r   r   r   r   r   r   rh   rX   r=   rH   r   rG   ifcfgif_typeethmacaddrr   bond_configv2_bond_mapr   bond_paramsparamr   newnameslave_interfacesr   portsr   match_prefixparams	br_configv2_bridge_mapnewvaluevalportportvalr   nscfgr   _namerT   r   r   r   r   r     s   




















zRenderer._render_contentr   )NN)F)__name__
__module____qualname____doc__r   r   r   r   r#   r   r   r   r   r   boolr   r   r   r   r   r   r   r   +  s$    
	
!
"r   c                 C   s2   dg}ddg}|D ]}t j||| ds dS q	dS )Nr   z	/usr/sbinz/sbin)r>   r`   FT)r   which)r`   expectedr>   pr   r   r   	available7  s   r
  r   )+r   rz   loggingra   r   tempfiler   typingr   r   r   	cloudinitr   r   r   r	   cloudinit.netr
   r   r   r   r   r   cloudinit.net.network_stater   r   rv   re   	getLoggerr  rA   r   r   rR   r[   rk   r#   r  r   r   r   r   r
  r   r   r   r   <module>   s2    
 
4
  