U
    Ld                  	   @   s  d dl Z d dlZd dlZd dl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
 d dlmZ d dlmZ d dlmZmZ d dlmZmZmZmZmZ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 m!Z! d d
l"m#Z# d dl$m%Z% erd dl&m'Z' e(e)Z*dZ+dZ,dZ-dZ.dZ/dZ0dZ1e#j2ddddZ3edZ4ede4f ede4f dddZ5e5dd Z6e5dd  Z7dd!e8e#j9d"d#d$Z:d%d& Z;e5e%d' fd(d)Z<e5d*d+ Z=e
d,d- Z>d.d/ Z?e5dd0d1d2e8e@eeA eBeBejCd3d4d5ZDe8e8e8eAd6d7d8ZEG d9d: d:ZFG d;d< d<eGZHG d=d> d>ZIG d?d@ d@ZJG dAdB dBZKG dCdD dDZLe5dTe8eee8  ee8 dEdFdGZMe5e8dHdIdJdKZNdLdM ZOG dNdO dOeGZPG dPdQ dQeGZQG dRdS dSZRdS )U    N)contextmanager)datetime)ENOENT)sleeptime)TYPE_CHECKINGCallableListOptionalTypeVarUnion)ElementTree)escape)distrossubp
temp_utils
url_helperutilversion)events)CFG_BUILTIN)errorsz168.63.129.16boot-telemetryzsystem-infoZ
diagnosticZ
compressedi  z'/run/cloud-init/log_pushed_to_kvp_indexzazure-dsz initialize reporter for azure dsT)namedescriptionZreporting_enabledT.)funcreturnc                    s    fdd}|S )Nc               
      s6   t j j jtd  | |W  5 Q R  S Q R X d S )Nr   r   parent)r   ReportEventStack__name__azure_ds_reporter)argskwargsr    A/usr/lib/python3/dist-packages/cloudinit/sources/helpers/azure.pyimpl5   s    z)azure_ds_telemetry_reporter.<locals>.implr&   )r   r(   r&   r%   r'   azure_ds_telemetry_reporter4   s    r)   c               
   C   s8  t  stdtd ztt tt  } W n, t	k
r` } ztd|W 5 d}~X Y nX zXt
j
ddddgd	d
\}}d}|rd|kr|dd }|std| t|d  }W nb t
jk
r } ztd| |W 5 d}~X Y n2 t	k
r } ztd| |W 5 d}~X Y nX z`t
j
dddddgd	d
\}}d}|r^d|kr^|dd }|sltd| t|d  }W nd t
jk
r } ztd| |W 5 d}~X Y n2 t	k
r } ztd| |W 5 d}~X Y nX ttddt|  d t| d t| d f tj}t| |S )z[Report timestamps related to kernel initialization and systemd
    activation of cloud-initz1distro not using systemd, skipping boot telemetryzCollecting boot telemetryz*Failed to determine kernel start timestampNZ	systemctlZshowz-pZUserspaceTimestampMonotonicT)capture=   z8Failed to parse UserspaceTimestampMonotonic from systemdi@B z-Failed to get UserspaceTimestampMonotonic: %sz<Failed to parse UserspaceTimestampMonotonic from systemd: %szcloud-init-localZInactiveExitTimestampMonotonicz;Failed to parse InactiveExitTimestampMonotonic from systemdz0Failed to get InactiveExitTimestampMonotonic: %sz?Failed to parse InactiveExitTimestampMonotonic from systemd: %sr   z5kernel_start=%s user_start=%s cloudinit_activation=%sZ)r   Zuses_systemdRuntimeErrorLOGdebugfloatr   r   Zuptime
ValueErrorr   splitZProcessExecutionErrorr   ReportingEventBOOT_EVENT_TYPEr   ZutcfromtimestampZ	isoformatDEFAULT_EVENT_ORIGINreport_event)Zkernel_starteout_ZtsmZ
user_startZcloudinit_activationevtr&   r&   r'   get_boot_telemetry@   s    





r<   c                  C   sb   t  } ttddt | d | d | d d | d d | d d | d	 f tj}t| |S )
z%Collect and report system informationzsystem informationztcloudinit_version=%s, kernel_version=%s, variant=%s, distro_name=%s, distro_version=%s, flavor=%s, python_version=%sreleaseZvariantZdistr   r,      python)	r   Zsystem_infor   r4   SYSTEMINFO_EVENT_TYPEr   Zversion_stringr6   r7   )infor;   r&   r&   r'   get_system_info   s$    



rB   logger_func)msgr   c                C   s6   t |r||  ttd| tj}tj|dhd |S )zReport a diagnostic eventzdiagnostic messagelogZexcluded_handler_types)callabler   r4   DIAGNOSTIC_EVENT_TYPEr6   r7   )rE   rD   r;   r&   r&   r'   report_diagnostic_event   s    rJ   c                 C   sP   t t|}d|dd}tt| t	|tj
}tj|dddhd |S )zReport a compressed eventzgz+b64ascii)encodingdatarF   printZwebhookrG   )base64Zencodebyteszlibcompressdecoder   r4   COMPRESSED_EVENT_TYPEjsondumpsr6   r7   )Z
event_nameZevent_contentZcompressed_dataZ
event_datar;   r&   r&   r'   report_compressed_event   s     rV   Zdef_log_filec              
   C   s@  t  }td zt| dt}|dtj t| t	 |}t
d| | |tjd ||tj td|  ttt|  W 5 Q R X W n8 tk
r } zt
dt| tjd W 5 d}~X Y nX td	 z$tjd
gddd\}}td
| W n: tk
r: } zt
dt| tjd W 5 d}~X Y nX dS )az  Push a portion of cloud-init.log file or the whole file to KVP
    based on the file size.
    The first time this function is called after VM boot, It will push the last
    n bytes of the log file such that n < MAX_LOG_TO_KVP_LENGTH
    If called again on the same boot, it continues from where it left off.
    In addition to cloud-init.log, dmesg log will also be collected.z"Dumping cloud-init.log file to KVPrbr   zMDumping last {0} bytes of cloud-init.log file to KVP starting from index: {1}rC   zcloud-init.logz#Exception when dumping log file: %sNzDumping dmesg log to KVPZdmesgFT)rR   r*   z$Exception when dumping dmesg log: %s)%get_last_log_byte_pushed_to_kvp_indexr/   r0   openseekosSEEK_ENDmaxtellMAX_LOG_TO_KVP_LENGTHrJ   formatSEEK_SETrV   readr   Z
write_fileLOG_PUSHED_TO_KVP_INDEX_FILEstr	Exceptionreprwarningr   )	file_nameZstart_indexfZ
seek_indexexr9   r:   r&   r&   r'   push_log_to_kvp   s<    	

 "


rk   c               
   C   s   z0t td} t|  W  5 Q R  W S Q R X W n tk
rr } z$|jtkrbtdt| t	j
d W 5 d }~X Y nn tk
r } ztdt| t	j
d W 5 d }~X Y n8 tk
r } ztdt| t	j
d W 5 d }~X Y nX dS )Nrz0Reading LOG_PUSHED_TO_KVP_INDEX_FILE failed: %s.rC   z2Invalid value in LOG_PUSHED_TO_KVP_INDEX_FILE: %s.z2Failed to get the last log byte pushed to KVP: %s.r   )rY   rc   intrb   IOErrorerrnor   rJ   rf   r/   rg   r2   re   )ri   r8   r&   r&   r'   rX      s(    $



rX   c              	   c   s6   t  }t t j|  z
d V  W 5 t | X d S N)r[   getcwdchdirpath
expanduser)ZnewdirZprevdirr&   r&   r'   cd  s
    
ru   c                 C   sx   |  dd}t|dkrdd}|dD ] }t|dkr>d| }||7 }q&tdt| ddd}n
|d	}t|S )
N\    :r,   0z>L   utf-8)	replacelenr3   structZpackrm   encodesocketZ	inet_ntoa)Zfallback_lease_valueZunescaped_valueZ
hex_stringZhex_pairZpacked_bytesr&   r&   r'   get_ip_from_lease_value!  s    

r         )rM   retry_sleeptimeout_minutes)urlheadersrM   r   r   r   c          	   
   C   s   |d t   }d}d}|s|d7 }ztj| ||dd}W qW n` tjk
r } z@td| |||j|jf tjd t  | |ksd	t	|kr W 5 d}~X Y nX t
| qtd
| |f tjd |S )zReadurl wrapper for querying wireserver.

    :param retry_sleep: Time to sleep before retrying.
    :param timeout_minutes: Retry up to specified number of minutes.
    :raises UrlError: on error fetching data.
    <   r   Nr,   )r   r   )r   rM   timeoutzdFailed HTTP request with Azure endpoint %s during attempt %d with exception: %s (code=%r headers=%r)rC   zNetwork is unreachablez@Successful HTTP request with Azure endpoint %s after %d attempts)r   r   ZreadurlZUrlErrorrJ   coder   r/   r0   rd   r   )	r   r   rM   r   r   r   Zattemptresponser8   r&   r&   r'   http_with_retries/  sB       

r   )usernamehostnamedisableSshPwdr   c                 C   s$   t d}|j| ||d}|dS )Na.          <ns0:Environment xmlns:ns0="http://schemas.dmtf.org/ovf/environment/1"
         xmlns:ns1="http://schemas.microsoft.com/windowsazure"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
          <ns1:ProvisioningSection>
            <ns1:Version>1.0</ns1:Version>
            <ns1:LinuxProvisioningConfigurationSet>
              <ns1:ConfigurationSetType>LinuxProvisioningConfiguration
              </ns1:ConfigurationSetType>
              <ns1:UserName>{username}</ns1:UserName>
              <ns1:DisableSshPasswordAuthentication>{disableSshPwd}
              </ns1:DisableSshPasswordAuthentication>
              <ns1:HostName>{hostname}</ns1:HostName>
            </ns1:LinuxProvisioningConfigurationSet>
          </ns1:ProvisioningSection>
          <ns1:PlatformSettingsSection>
            <ns1:Version>1.0</ns1:Version>
            <ns1:PlatformSettings>
              <ns1:ProvisionGuestAgent>true</ns1:ProvisionGuestAgent>
            </ns1:PlatformSettings>
          </ns1:PlatformSettingsSection>
        </ns0:Environment>
        )r   r   r   r|   )textwrapdedentr`   r   )r   r   r   ZOVF_ENV_TEMPLATEZretr&   r&   r'   build_minimal_ovff  s      r   c                   @   sH   e Zd ZdddZdd Zdejddd	Zdee	 ejdddZ
d
S )AzureEndpointHttpClientZWALinuxAgentz
2012-11-30)zx-ms-agent-namezx-ms-versionc                 C   s   d|d| _ d S )NZDES_EDE3_CBC)zx-ms-cipher-namez!x-ms-guest-agent-public-x509-cert)extra_secure_headers)selfcertificater&   r&   r'   __init__  s    z AzureEndpointHttpClient.__init__Fr   c                 C   s,   | j }|r | j  }|| j t||dS )N)r   )r   copyupdater   r   )r   r   securer   r&   r&   r'   get  s
    
zAzureEndpointHttpClient.getN)rM   r   c                 C   s0   | j }|d k	r"| j  }|| t|||dS )N)rM   r   )r   r   r   r   )r   r   rM   extra_headersr   r&   r&   r'   post  s
    

zAzureEndpointHttpClient.post)F)NN)r!   
__module____qualname__r   r   r   UrlResponser   r
   bytesr   r&   r&   r&   r'   r     s      r   c                   @   s   e Zd ZdZdS )InvalidGoalStateXMLExceptionz9Raised when GoalState XML is invalid or has missing data.N)r!   r   r   __doc__r&   r&   r&   r'   r     s   r   c                   @   s2   e Zd Zdeeef eeddddZdd Z	dS )		GoalStateTN)unparsed_xmlazure_endpoint_clientneed_certificater   c              
   C   s  || _ zt|| _W n8 tjk
rN } ztd| tjd  W 5 d}~X Y nX | d| _	| d| _
| d| _dD ]0}t| |dkrxd| }t|tjd t|qxd| _| d	}|dk	r|rtjd
dtd. | j j|ddj| _| jdkrtdW 5 Q R X dS )ah  Parses a GoalState XML string and returns a GoalState object.

        @param unparsed_xml: string representing a GoalState XML.
        @param azure_endpoint_client: instance of AzureEndpointHttpClient.
        @param need_certificate: switch to know if certificates is needed.
        @return: GoalState object representing the GoalState XML string.
        z!Failed to parse GoalState XML: %srC   Nz./Container/ContainerIdz4./Container/RoleInstanceList/RoleInstance/InstanceIdz./Incarnation)container_idinstance_idincarnationzMissing %s in GoalState XMLzD./Container/RoleInstanceList/RoleInstance/Configuration/Certificateszget-certificates-xmlzget certificates xmlr   T)r   z/Azure endpoint returned empty certificates xml.)r   r   
fromstringroot
ParseErrorrJ   r/   rg   _text_from_xpathr   r   r   getattrr   certificates_xmlr   r    r"   r   contents)r   r   r   r   r8   attrrE   r   r&   r&   r'   r     sJ    
 
zGoalState.__init__c                 C   s   | j |}|d k	r|jS d S rp   )r   findtext)r   Zxpathelementr&   r&   r'   r     s    zGoalState._text_from_xpath)T)
r!   r   r   r   rd   r   r   boolr   r   r&   r&   r&   r'   r     s    
7r   c                   @   s   e Zd ZdddZdd Zdd Zedd	 Zejd
d	 Ze	dd Z
ee	dd Ze	dd Ze	dd Ze	dd Ze	dd ZdS )OpenSSLManagerzTransportPrivate.pemzTransportCert.pem)private_keyr   c                 C   s   t  | _d | _|   d S rp   )r   Zmkdtemptmpdir_certificategenerate_certificater   r&   r&   r'   r     s    
zOpenSSLManager.__init__c                 C   s   t | j d S rp   )r   Zdel_dirr   r   r&   r&   r'   clean_up  s    zOpenSSLManager.clean_upc                 C   s   | j S rp   r   r   r&   r&   r'   r     s    zOpenSSLManager.certificatec                 C   s
   || _ d S rp   r   )r   valuer&   r&   r'   r     s    c                 C   s   t d | jd k	r"t d d S t| jj tddddddd	d
ddd| jd d| jd g d}t| jd D ]}d|krr|| 7 }qr|| _W 5 Q R X t d d S )Nz7Generating certificate for communication with fabric...zCertificate already generated.opensslZreqz-x509z-nodesz-subjz/CN=LinuxTransportz-daysZ32768z-newkeyzrsa:2048z-keyoutr   z-outr   rw   ZCERTIFICATEzNew certificate generated.)	r/   r0   r   ru   r   r   certificate_namesrY   rstrip)r   r   liner&   r&   r'   r      s8    


z#OpenSSLManager.generate_certificatec                 C   s"   ddd| g}t j ||d\}}|S )Nr   Zx509z-nooutrM   )r   )actionZcertcmdresultr:   r&   r&   r'   _run_x509_action   s    zOpenSSLManager._run_x509_actionc                 C   s2   |  d|}ddddddg}tj||d\}}|S )	Nz-pubkeyz
ssh-keygenz-iz-mZPKCS8z-fz
/dev/stdinr   )r   r   )r   r   Zpub_keyZ
keygen_cmdssh_keyr:   r&   r&   r'   _get_ssh_key_from_cert'  s    z%OpenSSLManager._get_ssh_key_from_certc                 C   s6   |  d|}|d}||d d d}d|S )a   openssl x509 formats fingerprints as so:
        'SHA1 Fingerprint=07:3E:19:D1:4D:1C:79:92:24:C6:A0:FD:8D:DA:        B6:A8:BF:27:D4:73
'

        Azure control plane passes that fingerprint as so:
        '073E19D14D1C799224C6A0FD8DDAB6A8BF27D473'
        z-fingerprintr+   r,   ry   rw   )r   r   r3   join)r   r   Zraw_fpeqZoctetsr&   r&   r'   _get_fingerprint_from_cert.  s    	
z)OpenSSLManager._get_fingerprint_from_certc              	   C   sj   t |d}|j}ddddd|dg}t| j* tjdjf | j	d	d

|d\}}W 5 Q R X |S )zDecrypt the certificates XML document using the our private key;
        return the list of certs and private keys contained in the doc.
        z.//Datas   MIME-Version: 1.0s<   Content-Disposition: attachment; filename="Certificates.p7m"s?   Content-Type: application/x-pkcs7-mime; name="Certificates.p7m"s!   Content-Transfer-Encoding: base64    r|   zuopenssl cms -decrypt -in /dev/stdin -inkey {private_key} -recip {certificate} | openssl pkcs12 -nodes -password pass:T   
)shellrM   )r   r   r   r   r   ru   r   r   r`   r   r   )r   r   tagZcertificates_contentlinesr9   r:   r&   r&   r'   _decrypt_certs_from_xml<  s$    z&OpenSSLManager._decrypt_certs_from_xmlc           	      C   sv   |  |}g }i }| D ]V}|| td|r:g }qtd|rd|}| |}| |}|||< g }q|S )zGiven the Certificates XML document, return a dictionary of
        fingerprints and associated SSH keys derived from the certs.z[-]+END .*?KEY[-]+$z[-]+END .*?CERTIFICATE[-]+$
)r   
splitlinesappendrematchr   r   r   )	r   r   r9   Zcurrentkeysr   r   r   fingerprintr&   r&   r'   parse_certificatesU  s    




z!OpenSSLManager.parse_certificatesN)r!   r   r   r   r   r   propertyr   setterr)   r   staticmethodr   r   r   r   r   r&   r&   r&   r'   r     s,   





r   c                   @   s   e Zd ZedZedZdZdZdZ	dZ
eeeddd	d
ZeddddZeeddddZdeeeeedddZeeddddZdS )GoalStateHealthReportera          <?xml version="1.0" encoding="utf-8"?>
        <Health xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns:xsd="http://www.w3.org/2001/XMLSchema">
          <GoalStateIncarnation>{incarnation}</GoalStateIncarnation>
          <Container>
            <ContainerId>{container_id}</ContainerId>
            <RoleInstanceList>
              <Role>
                <InstanceId>{instance_id}</InstanceId>
                <Health>
                  <State>{health_status}</State>
                  {health_detail_subsection}
                </Health>
              </Role>
            </RoleInstanceList>
          </Container>
        </Health>
        z        <Details>
          <SubStatus>{health_substatus}</SubStatus>
          <Description>{health_description}</Description>
        </Details>
        ZReadyZNotReadyZProvisioningFailedi   N)
goal_stater   endpointr   c                 C   s   || _ || _|| _dS )a?  Creates instance that will report provisioning status to an endpoint

        @param goal_state: An instance of class GoalState that contains
            goal state info such as incarnation, container id, and instance id.
            These 3 values are needed when reporting the provisioning status
            to Azure
        @param azure_endpoint_client: Instance of class AzureEndpointHttpClient
        @param endpoint: Endpoint (string) where the provisioning status report
            will be sent to
        @return: Instance of class GoalStateHealthReporter
        N)_goal_state_azure_endpoint_client	_endpoint)r   r   r   r   r&   r&   r'   r     s    z GoalStateHealthReporter.__init__r   c              
   C   s   | j | jj| jj| jj| jd}td z| j|d W n6 t	k
rp } zt
d| tjd  W 5 d }~X Y nX td d S )N)r   r   r   statusz Reporting ready to Azure fabric.documentz#exception while reporting ready: %srC   zReported ready to Azure fabric.)build_reportr   r   r   r   PROVISIONING_SUCCESS_STATUSr/   r0   _post_health_reportre   rJ   errorrA   )r   r   r8   r&   r&   r'   send_ready_signal  s     
z)GoalStateHealthReporter.send_ready_signalr   r   c              
   C   s   | j | jj| jj| jj| j| j|d}z| j|d W n: tk
rp } zd| }t	|t
jd  W 5 d }~X Y nX t
d d S )N)r   r   r   r   	substatusr   r   z%exception while reporting failure: %srC   z!Reported failure to Azure fabric.)r   r   r   r   r   PROVISIONING_NOT_READY_STATUSPROVISIONING_FAILURE_SUBSTATUSr   re   rJ   r/   r   rg   )r   r   r   r8   rE   r&   r&   r'   send_failure_signal  s    z+GoalStateHealthReporter.send_failure_signal)r   r   r   r   r   c           	      C   sb   d}|d k	r.| j jt|t|d | j d}| jjtt|t|t|t||d}|dS )Nrw   )Zhealth_substatusZhealth_description)r   r   r   Zhealth_statusZhealth_detail_subsectionr|   )%HEALTH_DETAIL_SUBSECTION_XML_TEMPLATEr`   r   "HEALTH_REPORT_DESCRIPTION_TRIM_LENHEALTH_REPORT_XML_TEMPLATErd   r   )	r   r   r   r   r   r   r   Zhealth_detailZhealth_reportr&   r&   r'   r     s     	
z$GoalStateHealthReporter.build_report)r   r   c                 C   sH   t   td td d| j}| jj||ddid td d S )Nr   z&Sending health report to Azure fabric.zhttp://{}/machine?comp=healthzContent-Typeztext/xml; charset=utf-8)rM   r   z/Successfully sent health report to Azure fabric)rk   r   r/   r0   r`   r   r   r   )r   r   r   r&   r&   r'   r     s    
z+GoalStateHealthReporter._post_health_report)NN)r!   r   r   r   r   r   r   r   r   r   r   r   r   rd   r   r)   r   r   r   r   r   r&   r&   r&   r'   r   j  s<   	  r   c                   @   s   e Zd ZedddZdd Zedddd	Zedee	e  dd
dZ
eeddddZeeedddZeedddZeeeef eedddZeeeedddZeeeedddZdS )WALinuxAgentShimr   c                 C   s   || _ d | _d | _d S rp   )r   openssl_managerr   )r   r   r&   r&   r'   r     s    zWALinuxAgentShim.__init__c                 C   s   | j d k	r| j   d S rp   )r   r   r   r&   r&   r'   r     s    
zWALinuxAgentShim.clean_upNr   c              
   C   sV   zt d td|g W n4 tk
rP } ztd| t jd W 5 d }~X Y nX d S )NzEjecting the provisioning isoZejectz(Failed ejecting the provisioning iso: %srC   )r/   r0   r   re   rJ   )r   iso_devr8   r&   r&   r'   	eject_iso  s    
zWALinuxAgentShim.eject_isoc                 C   s   d}| j dkr&|dk	r&t | _ | j j}| jdkr:t|| _| j|dk	d}d}|dk	rb| ||}t|| j| j}|dk	r| 	| |
  |S )a  Gets the VM's GoalState from Azure, uses the GoalState information
        to report ready/send the ready signal/provisioning complete signal to
        Azure, and then uses pubkey_info to filter and obtain the user's
        pubkeys from the GoalState.

        @param pubkey_info: List of pubkey values and fingerprints which are
            used to filter and obtain the user's pubkey values from the
            GoalState.
        @return: The list of user's authorized pubkey values.
        Nr   )r   r   r   r   r   _fetch_goal_state_from_azure_get_user_pubkeysr   r   r   r   )r   pubkey_infor   Zhttp_client_certificater   ssh_keyshealth_reporterr&   r&   r'   "register_with_azure_and_fetch_data   s.    
  
z3WALinuxAgentShim.register_with_azure_and_fetch_datar   c                 C   s@   | j dkrtd| _ | jdd}t|| j | j}|j|d dS )zGets the VM's GoalState from Azure, uses the GoalState information
        to report failure/send provisioning failure signal to Azure.

        @param: user visible error description of provisioning failure.
        NFr   r   )r   r   r   r   r   r   )r   r   r   r  r&   r&   r'   &register_with_azure_and_report_failureF  s    

  z7WALinuxAgentShim.register_with_azure_and_report_failure)r   r   c                 C   s   |   }| ||S )a   Fetches the GoalState XML from the Azure endpoint, parses the XML,
        and returns a GoalState object.

        @param need_certificate: switch to know if certificates is needed.
        @return: GoalState object representing the GoalState XML
        )"_get_raw_goal_state_xml_from_azure_parse_raw_goal_state_xml)r   r   unparsed_goal_state_xmlr&   r&   r'   r   U  s
    
 z-WALinuxAgentShim._fetch_goal_state_from_azurec              
   C   s   t d d| j}z,tjddtd | j|}W 5 Q R X W n6 t	k
rx } zt
d| t jd  W 5 d}~X Y nX t d	 |jS )
zFetches the GoalState XML from the Azure endpoint and returns
        the XML as a string.

        @return: GoalState XML string
        zRegistering with Azure...z!http://{}/machine/?comp=goalstatezgoalstate-retrievalzretrieve goalstater   z9failed to register with Azure and fetch GoalState XML: %srC   Nz#Successfully fetched GoalState XML.)r/   rA   r`   r   r   r    r"   r   r   re   rJ   rg   r0   r   )r   r   r   r8   r&   r&   r'   r  d  s&    

z3WALinuxAgentShim._get_raw_goal_state_xml_from_azure)r  r   r   c              
   C   s~   zt || j|}W n6 tk
rH } ztd| tjd  W 5 d}~X Y nX dd|j d|j d|j	 g}t|tj
d |S )a  Parses a GoalState XML string and returns a GoalState object.

        @param unparsed_goal_state_xml: GoalState XML string
        @param need_certificate: switch to know if certificates is needed.
        @return: GoalState object representing the GoalState XML
        z"Error processing GoalState XML: %srC   Nz, zGoalState XML container id: %szGoalState XML instance id: %szGoalState XML incarnation: %s)r   r   re   rJ   r/   rg   r   r   r   r   r0   )r   r  r   r   r8   rE   r&   r&   r'   r    s(    z*WALinuxAgentShim._parse_raw_goal_state_xml)r   r   r   c                 C   sH   g }|j dk	rD|dk	rD| jdk	rDtd | j|j }| ||}|S )a  Gets and filters the VM admin user's authorized pubkeys.

        The admin user in this case is the username specified as "admin"
        when deploying VMs on Azure.
        See https://docs.microsoft.com/en-us/cli/azure/vm#az-vm-create.
        cloud-init expects a straightforward array of keys to be dropped
        into the admin user's authorized_keys file. Azure control plane exposes
        multiple public keys to the VM via wireserver. Select just the
        admin user's key(s) and return them, ignoring any other certs.

        @param goal_state: GoalState object. The GoalState object contains
            a certificate XML, which contains both the VM user's authorized
            pubkeys and other non-user pubkeys, which are used for
            MSI and protected extension handling.
        @param pubkey_info: List of VM user pubkey dicts that were previously
            obtained from provisioning data.
            Each pubkey dict in this list can either have the format
            pubkey['value'] or pubkey['fingerprint'].
            Each pubkey['fingerprint'] in the list is used to filter
            and obtain the actual pubkey value from the GoalState
            certificates XML.
            Each pubkey['value'] requires no further processing and is
            immediately added to the return list.
        @return: A list of the VM user's authorized pubkey values.
        Nz/Certificate XML found; parsing out public keys.)r   r   r/   r0   r   _filter_pubkeys)r   r   r   r  keys_by_fingerprintr&   r&   r'   r     s    
z"WALinuxAgentShim._get_user_pubkeys)r
  r   r   c                 C   s|   g }|D ]n}d|kr,|d r,| |d  qd|krj|d rj|d }|| kr\| | |  qvtd| qtd| q|S )a8  Filter and return only the user's actual pubkeys.

        @param keys_by_fingerprint: pubkey fingerprint -> pubkey value dict
            that was obtained from GoalState Certificates XML. May contain
            non-user pubkeys.
        @param pubkey_info: List of VM user pubkeys. Pubkey values are added
            to the return list without further processing. Pubkey fingerprints
            are used to filter and obtain the actual pubkey values from
            keys_by_fingerprint.
        @return: A list of the VM user's authorized pubkey values.
        r   r   zIovf-env.xml specified PublicKey fingerprint %s not found in goalstate XMLzFovf-env.xml specified PublicKey with neither value nor fingerprint: %s)r   r/   rg   )r
  r   r   Zpubkeyr   r&   r&   r'   r	    s"    z WALinuxAgentShim._filter_pubkeys)NN)r!   r   r   rd   r   r   r)   r   r
   r	   r  r  r   r   r   r   r  r   r  listr   r   dictr	  r&   r&   r&   r'   r     s<   
   
%
! )r   )r   r   r   c                 C   s,   t | d}z|j||dW S |  X d S )Nr   )r   r   )r   r   r  )r   r   r   shimr&   r&   r'   get_metadata_from_fabric  s    
 
r  zerrors.ReportableError)r   r   c                 C   s2   t | d}| }z|j|d W 5 |  X d S )Nr   r  )r   Zas_encoded_reportr   r  )r   r   r  r   r&   r&   r'   report_failure_to_fabric  s
    
r  c                 C   s(   t d|  tjd t d| tjd d S )Nzdhclient output stream: %srC   zdhclient error stream: %s)rJ   r/   r0   )r9   errr&   r&   r'   dhcp_log_cb	  s      r  c                   @   s   e Zd ZdS )BrokenAzureDataSourceNr!   r   r   r&   r&   r&   r'   r    s   r  c                   @   s   e Zd ZdS )NonAzureDataSourceNr  r&   r&   r&   r'   r    s   r  c                   @   s   e Zd ZdddZdddddddddee ee ee ee ee eee	  eee dd	dd	Z
ed
ddZeed dddZdeeedddZdeeeedddZdd Zdd Zdd ZdS )	OvfEnvXmlz)http://schemas.dmtf.org/ovf/environment/1z)http://schemas.microsoft.com/windowsazure)ZovfwaNFr   passwordr   custom_datadisable_ssh_password_authpublic_keyspreprovisioned_vmpreprovisioned_vm_type)	r   r  r   r  r  r  r  r  r   c          	      C   s8   || _ || _|| _|| _|| _|p$g | _|| _|| _d S rp   r  )	r   r   r  r   r  r  r  r  r  r&   r&   r'   r      s    
zOvfEnvXml.__init__r   c                 C   s   | j |j kS rp   )__dict__)r   otherr&   r&   r'   __eq__5  s    zOvfEnvXml.__eq__)ovf_env_xmlr   c              
   C   sz   zt |}W n6 t jk
rD } zd| }t||W 5 d}~X Y nX |d| js\tdt }|| |	| |S )zParser for ovf-env.xml data.

        :raises NonAzureDataSource: if XML is not in Azure's format.
        :raises BrokenAzureDataSource: if XML is unparseable or invalid.
        zInvalid ovf-env.xml: %sNz./wa:ProvisioningSectionz=Ignoring non-Azure ovf-env.xml: ProvisioningSection not found)
r   r   r   r  r   
NAMESPACESr  r  &_parse_linux_configuration_set_section _parse_platform_settings_section)clsr!  r   r8   Z	error_strinstancer&   r&   r'   
parse_text8  s    

zOvfEnvXml.parse_textr  )r   required	namespacec                 C   sl   | d||f tj}t|dkrDd| }t| |r@t|d S t|dkrdtd|t|f |d S )Nz./%s:%sr   #No ovf-env.xml configuration for %rr,   :Multiple configuration matches in ovf-exml.xml for %r (%d))findallr  r"  r~   r/   r0   r  )r   noder   r(  r)  matchesrE   r&   r&   r'   _findQ  s"    
 

zOvfEnvXml._find)r   r(  decode_base64
parse_boolc           
      C   s   | d| tj}t|dkr@d| }t| |r<t||S t|dkr`td|t|f |d j}	|	d krv|}	|r|	d k	rt	d
|	 }	|rt|	}	|	S )Nz./wa:r   r*  r,   r+  rw   )r,  r  r"  r~   r/   r0   r  r   rO   Z	b64decoder   r3   r   Ztranslate_bool)
r   r-  r   r(  r0  r1  defaultr.  rE   r   r&   r&   r'   _parse_propertyi  s*    	



zOvfEnvXml._parse_propertyc                 C   s   | j |ddd}| j |ddd}| j|dddd| _| j|ddd| _| j|d	dd| _| j|d
dd| _| j|dddd| _| | d S )NZProvisioningSectionTr(  Z!LinuxProvisioningConfigurationSetZ
CustomDataF)r0  r(  ZUserNameZUserPasswordZHostNameZ DisableSshPasswordAuthentication)r1  r(  )r/  r3  r  r   r  r   r  _parse_ssh_section)r   r   Zprovisioning_section
config_setr&   r&   r'   r#    sL            z0OvfEnvXml._parse_linux_configuration_set_sectionc                 C   sL   | j |ddd}| j |ddd}| j|ddddd| _| j|ddd| _d S )	NZPlatformSettingsSectionTr4  ZPlatformSettingsZPreprovisionedVmF)r1  r2  r(  ZPreprovisionedVMType)r/  r3  r  r  )r   r   Zplatform_settings_sectionZplatform_settingsr&   r&   r'   r$    s,        z*OvfEnvXml._parse_platform_settings_sectionc           	      C   s   g | _ | j|ddd}|d kr"d S | j|ddd}|d kr>d S |dtjD ]N}| j|ddd}| j|ddd}| j|dd	dd
}|||d}| j | qLd S )NZSSHFr4  Z
PublicKeysz./wa:PublicKeyZFingerprintPathZValuerw   )r2  r(  )r   rs   r   )r  r/  r,  r  r"  r3  r   )	r   r6  Zssh_sectionZpublic_keys_sectionZ
public_keyr   rs   r   r   r&   r&   r'   r5    s@            zOvfEnvXml._parse_ssh_section)r  )FFN)r!   r   r   r"  r
   rd   r   r   r	   r  r   r   classmethodr'  r/  r3  r#  r$  r5  r&   r&   r&   r'   r    sV   
    $"r  )NN)SrO   rT   Zloggingr[   r   r   r   r   rP   
contextlibr   r   ro   r   r   r   typingr   r   r	   r
   r   r   Z	xml.etreer   Zxml.sax.saxutilsr   Z	cloudinitr   r   r   r   r   r   Zcloudinit.reportingr   Zcloudinit.settingsr   Zcloudinit.sources.azurer   Z	getLoggerr!   r/   ZDEFAULT_WIRESERVER_ENDPOINTr5   r@   rI   rS   r_   rc   r    r"   r   r)   r<   rB   rd   r4   rJ   rV   rk   rX   ru   r   r  r   rm   r   r   r   r   re   r   r   r   r   r   r  r  r  r  r  r  r&   r&   r&   r'   <module>   s     
 
S
(

	7  "?  " f  
		