o
    w7e                     @   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r~d 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Z4dede4f dede4f fddZ5e5dd Z6e5d d! Z7dd"d#e8de#j9fd$d%Z:d&d' Z;e5e%d( fd)d*Z<e5d+d, Z=e
d-d. Z>d/d0 Z?e5dd1d2d3d4e8d5e@d6eeA d7eBd8eBdejCfd9d:ZDd;e8d<e8d=e8deAfd>d?ZEG d@dA dAZFG dBdC dCeGZHG dDdE dEZIG dFdG dGZJG dHdI dIZKG dJdK dKZLe5		d]dLe8dMeee8  dNee8 fdOdPZMe5dLe8dQdRfdSdTZNdUdV ZOG dWdX dXeGZPG dYdZ dZeGZQG d[d\ d\ZRdS )^    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-info
diagnostic
compressedi  z'/run/cloud-init/log_pushed_to_kvp_indexzazure-dsz initialize reporter for azure dsT)namedescriptionreporting_enabledTfunc.returnc                    s    fdd}|S )Nc                     sF   t j j jtd  | i |W  d    S 1 sw   Y  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   s  t  stdtd ztt tt  } W n t	y+ } ztd|d}~ww z*t
j
g ddd\}}d}|rGd|v rG|dd	 }|sMtd
| t|d  }W n& t
jyj } ztd| |d}~w t	y| } ztd| |d}~ww z*t
j
g ddd\}}d}|rd|v r|dd	 }|std| t|d  }W n& t
jy } ztd| |d}~w t	y } ztd| |d}~ww 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 timestampN)	systemctlshow-pUserspaceTimestampMonotonicT)capture=   z8Failed to parse UserspaceTimestampMonotonic from systemdi@B z-Failed to get UserspaceTimestampMonotonic: %sz<Failed to parse UserspaceTimestampMonotonic from systemd: %s)r-   r.   zcloud-init-localr/   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   uses_systemdRuntimeErrorLOGdebugfloatr   r   uptime
ValueErrorr   splitProcessExecutionErrorr   ReportingEventBOOT_EVENT_TYPEr   utcfromtimestamp	isoformatDEFAULT_EVENT_ORIGINreport_event)kernel_starteout_tsm
user_startcloudinit_activationevtr)   r)   r*   get_boot_telemetry@   s   





rM   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releasevariantdistr   r3      python)	r   system_infor   r?   SYSTEMINFO_EVENT_TYPEr   version_stringrC   rD   )inforL   r)   r)   r*   get_system_info   s$   



rW   logger_funcmsgc                C   s6   t |r||  ttd| tj}tj|dhd |S )zReport a diagnostic eventzdiagnostic messagelogexcluded_handler_types)callabler   r?   DIAGNOSTIC_EVENT_TYPErC   rD   )rZ   rY   rL   r)   r)   r*   report_diagnostic_event   s   r`   c                 C   sN   t t|}d|dd}tt| t	|tj
}tj|h dd |S )zReport a compressed eventzgz+b64ascii)encodingdata>   r[   printwebhookr\   )base64encodebyteszlibcompressdecoder   r?   COMPRESSED_EVENT_TYPEjsondumpsrC   rD   )
event_nameevent_contentcompressed_data
event_datarL   r)   r)   r*   report_compressed_event   s   rr   def_log_filec              
   C   sV  t  }td zNt| d?}|dtj t| t	 |}t
d| | |tjd ||tj td|  ttt|  W d   n1 sPw   Y  W n tys } zt
dt| tjd W Y d}~nd}~ww td	 ztjd
gddd\}}td
| W dS  ty } zt
dt| tjd W Y d}~dS d}~ww )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}rX   zcloud-init.logNz#Exception when dumping log file: %szDumping dmesg log to KVPdmesgFT)rj   r1   z$Exception when dumping dmesg log: %s)%get_last_log_byte_pushed_to_kvp_indexr8   r9   openseekosSEEK_ENDmaxtellMAX_LOG_TO_KVP_LENGTHr`   formatSEEK_SETrr   readr   
write_fileLOG_PUSHED_TO_KVP_INDEX_FILEstr	Exceptionreprwarningr   )	file_namestart_indexf
seek_indexexrG   rH   r)   r)   r*   push_log_to_kvp   sD   	



r   c               
   C   s  zt td} t|  W  d    W S 1 sw   Y  W dS  tyI } z|jtkr>tdt| t	j
d W Y d }~dS W Y d }~dS d }~w tyf } ztdt| t	j
d W Y d }~dS d }~w ty } ztdt| t	j
d W Y d }~dS d }~ww )Nrz0Reading LOG_PUSHED_TO_KVP_INDEX_FILE failed: %s.rX   z2Invalid value in LOG_PUSHED_TO_KVP_INDEX_FILE: %s.z2Failed to get the last log byte pushed to KVP: %s.r   )rw   r   intr   IOErrorerrnor   r`   r   r8   r   r<   r   )r   rF   r)   r)   r*   rv      s<   
$



	
rv   c              	   c   s@    t  }t t j|  zd V  W t | d S t | w N)ry   getcwdchdirpath
expanduser)newdirprevdirr)   r)   r*   cd  s   r   c                 C   sx   |  dd}t|dkr2d}|dD ]}t|dkrd| }||7 }qtdt| ddd}n|d	}t|S )
N\    :r3   0z>L   utf-8)	replacelenr=   structpackr   encodesocket	inet_ntoa)fallback_lease_valueunescaped_value
hex_stringhex_pairpacked_bytesr)   r)   r*   get_ip_from_lease_value!  s   


r         )rc   retry_sleeptimeout_minutesurlheadersrc   r   r   c          	   
   C   s   |d t   }d}d}|sT|d7 }ztj| ||dd}W n7 tjyM } z$td| |||j|jf tjd t  | |ksBd	t	|v rC W Y d}~nd}~ww t
| |r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   Nr3   )r   r   )r   rc   timeoutzdFailed HTTP request with Azure endpoint %s during attempt %d with exception: %s (code=%r headers=%r)rX   zNetwork is unreachablez@Successful HTTP request with Azure endpoint %s after %d attempts)r   r   readurlUrlErrorr`   coder   r8   r9   r   r   )	r   r   rc   r   r   r   attemptresponserF   r)   r)   r*   http_with_retries/  s@   r   usernamehostnamedisableSshPwdc                 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   OVF_ENV_TEMPLATEretr)   r)   r*   build_minimal_ovff  s   
r   c                   @   sL   e Zd ZdddZdd Zddejfdd	Z	
ddee	 dejfddZ
d
S )AzureEndpointHttpClientWALinuxAgentz
2012-11-30)zx-ms-agent-namezx-ms-versionc                 C   s   d|d| _ d S )N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__Fr    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.getNrc   c                 C   s0   | j }|d ur| j  }|| t|||dS )N)rc   r   )r   r   r   r   )r   r   rc   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                	   @   s:   e Zd Z	ddeeef dededdfddZd	d
 Z	dS )	GoalStateTunparsed_xmlazure_endpoint_clientneed_certificater    Nc              
   C   s   || _ zt|| _W n tjy" } z
td| tjd  d}~ww | d| _	| d| _
| d| _dD ]}t| |du rOd| }t|tjd t|q7d| _| d	}|dur|rtjd
dtd | j j|ddj| _| jdu rztdW d   dS 1 sw   Y  dS dS 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: %srX   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
ParseErrorr`   r8   r   _text_from_xpathr   r   r   getattrr   certificates_xmlr   r#   r%   r   contents)r   r   r   r   rF   attrrZ   r   r)   r)   r*   r     sX   
"zGoalState.__init__c                 C   s   | j |}|d ur|jS d S r   )r   findtext)r   xpathelementr)   r)   r*   r     s   zGoalState._text_from_xpath)T)
r$   r   r   r   r   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 r   )r   mkdtemptmpdir_certificategenerate_certificater   r)   r)   r*   r     s   
zOpenSSLManager.__init__c                 C   s   t | j d S r   )r   del_dirr   r   r)   r)   r*   clean_up  s   zOpenSSLManager.clean_upc                 C   s   | j S r   r   r   r)   r)   r*   r     s   zOpenSSLManager.certificatec                 C   s
   || _ d S r   r   )r   valuer)   r)   r*   r     s   
c                 C   s   t d | jd urt d d S t| 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|vrE|| 7 }q9|| _W d    n1 sSw   Y  t d d S )Nz7Generating certificate for communication with fabric...zCertificate already generated.opensslreqz-x509z-nodesz-subjz/CN=LinuxTransportz-days32768z-newkeyzrsa:2048z-keyoutr   z-outr   r   CERTIFICATEzNew certificate generated.)	r8   r9   r   r   r   r   certificate_namesrw   rstrip)r   r   liner)   r)   r*   r      s<   


z#OpenSSLManager.generate_certificatec                 C   s"   ddd| g}t j ||d\}}|S )Nr   x509z-nooutrc   )r   )actioncertcmdresultrH   r)   r)   r*   _run_x509_action   s   zOpenSSLManager._run_x509_actionc                 C   s*   |  d|}g d}tj||d\}}|S )Nz-pubkey)z
ssh-keygenz-iz-mPKCS8z-fz
/dev/stdinr   )r  r   )r   r   pub_key
keygen_cmdssh_keyrH   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-fingerprintr2   r3   r   r   )r  r   r=   join)r   r   raw_fpeqoctetsr)   r)   r*   _get_fingerprint_from_cert.  s   	

z)OpenSSLManager._get_fingerprint_from_certc                 C   s   t |d}|j}ddddd|dg}t| j tjdjdi | j	d	d

|d\}}W d   |S 1 s;w   Y  |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   
)shellrc   Nr)   )r   r   r   r   r   r   r   r   r~   r   r
  )r   r   tagcertificates_contentlinesrG   rH   r)   r)   r*   _decrypt_certs_from_xml<  s.   
z&OpenSSLManager._decrypt_certs_from_xmlc           	      C   sv   |  |}g }i }| D ]+}|| td|rg }qtd|r8d|}| |}| |}|||< 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   rG   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
deded	ed
dfddZedddZeded
dfddZ		ddedededed
ef
ddZeded
df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>
        ReadyNotReadyProvisioningFailedi   
goal_stater   endpointr    Nc                 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__c              
   C   sv   | j | jj| jj| jj| jd}td z| j|d W n t	y3 } z
t
d| tjd  d }~ww td d S )N)r   r   r   statusz Reporting ready to Azure fabric.documentz#exception while reporting ready: %srX   zReported ready to Azure fabric.)build_reportr(  r   r   r   PROVISIONING_SUCCESS_STATUSr8   r9   _post_health_reportr   r`   errorrV   )r   r-  rF   r)   r)   r*   send_ready_signal  s$   
z)GoalStateHealthReporter.send_ready_signalr   c              
   C   sv   | j | jj| jj| jj| j| j|d}z| j|d W n ty3 } zd| }t	|t
jd  d }~ww t
d d S )N)r   r   r   r+  	substatusr   r,  z%exception while reporting failure: %srX   z!Reported failure to Azure fabric.)r.  r(  r   r   r   PROVISIONING_NOT_READY_STATUSPROVISIONING_FAILURE_SUBSTATUSr0  r   r`   r8   r1  r   )r   r   r-  rF   rZ   r)   r)   r*   send_failure_signal  s"   z+GoalStateHealthReporter.send_failure_signalr   r   r   r+  c           	      C   sb   d}|d ur| j jt|t|d | j d}| jjtt|t|t|t||d}|dS )Nr   )health_substatushealth_description)r   r   r   health_statushealth_detail_subsectionr   )%HEALTH_DETAIL_SUBSECTION_XML_TEMPLATEr~   r   "HEALTH_REPORT_DESCRIPTION_TRIM_LENHEALTH_REPORT_XML_TEMPLATEr   r   )	r   r   r   r   r+  r3  r   health_detailhealth_reportr)   r)   r*   r.    s    	

z$GoalStateHealthReporter.build_reportr-  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)rc   r   z/Successfully sent health report to Azure fabric)r   r   r8   r9   r~   r*  r)  r   )r   r-  r   r)   r)   r*   r0    s   
z+GoalStateHealthReporter._post_health_reportr    Nr   )r$   r   r   r   r   r=  r;  r/  r4  r5  r<  r   r   r   r   r,   r2  r6  r   r.  r0  r)   r)   r)   r*   r"  j  sN    	

r"  c                   @   s   e Zd ZdefddZdd Zeddd	Ze	ddee	e  fd
dZ
ededdfddZededefddZedefddZedeeef dedefddZedededefddZedededefddZdS ) WALinuxAgentShimr'  c                 C   s   || _ d | _d | _d S r   )r'  openssl_managerr   )r   r'  r)   r)   r*   r     s   
zWALinuxAgentShim.__init__c                 C   s   | j d ur| j   d S d S r   )rB  r   r   r)   r)   r*   r     s   
zWALinuxAgentShim.clean_upr    Nc              
   C   sX   zt d td|g W d S  ty+ } ztd| t jd W Y d }~d S d }~ww )NzEjecting the provisioning isoejectz(Failed ejecting the provisioning iso: %srX   )r8   r9   r   r   r`   )r   iso_devrF   r)   r)   r*   	eject_iso  s   
zWALinuxAgentShim.eject_isoc                 C   s   d}| j du r|durt | _ | j j}| jdu rt|| _| j|dud}d}|dur1| ||}t|| j| j}|durB| 	| |
  |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   )rB  r   r   r   r   _fetch_goal_state_from_azure_get_user_pubkeysr"  r'  rE  r2  )r   pubkey_inforD  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u 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.
        NFrF  r   )r   r   rG  r"  r'  r6  )r   r   r&  rL  r)   r)   r*   &register_with_azure_and_report_failureF  s   


z7WALinuxAgentShim.register_with_azure_and_report_failurer   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*   rG  U  s   
z-WALinuxAgentShim._fetch_goal_state_from_azurec              
   C   s   t d d| j}z tjddtd | j|}W d   n1 s%w   Y  W n t	yA } z
t
d| t jd  d}~ww 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!   Nz9failed to register with Azure and fetch GoalState XML: %srX   z#Successfully fetched GoalState XML.)r8   rV   r~   r'  r   r#   r%   r   r   r   r`   r   r9   r   )r   r   r   rF   r)   r)   r*   rP  d  s.   

z3WALinuxAgentShim._get_raw_goal_state_xml_from_azurerR  c              
   C   st   z	t || j|}W n ty } z
td| tjd  d}~ww 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: %srX   Nz, zGoalState XML container id: %szGoalState XML instance id: %szGoalState XML incarnation: %s)r   r   r   r`   r8   r   r
  r   r   r   r9   )r   rR  r   r&  rF   rZ   r)   r)   r*   rQ    s,   z*WALinuxAgentShim._parse_raw_goal_state_xmlr&  rI  c                 C   sH   g }|j dur"|dur"| jdur"t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   rB  r8   r9   r  _filter_pubkeys)r   r&  rI  rK  keys_by_fingerprintr)   r)   r*   rH    s   


z"WALinuxAgentShim._get_user_pubkeysrT  c                 C   s|   g }|D ]7}d|v r|d r| |d  qd|v r5|d r5|d }|| v r.| | |  qt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  r8   r   )rT  rI  r  pubkeyr  r)   r)   r*   rS    s"   z WALinuxAgentShim._filter_pubkeysr@  r   )r$   r   r   r   r   r   r,   rE  r
   r	   rM  rO  r   r   rG  r   rP  r   rQ  listrH  r!  dictrS  r)   r)   r)   r*   rA    sL    

%
!)rA  r'  rI  rD  c                 C   s.   t | d}z|j||dW |  S |  w )Nr'  )rI  rD  )rA  rM  r   )r'  rI  rD  shimr)   r)   r*   get_metadata_from_fabric  s   
rZ  r1  zerrors.ReportableErrorc                 C   s8   t | d}| }z|j|d W |  d S |  w )NrX  rN  )rA  as_encoded_reportrO  r   )r'  r1  rY  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: %srX   zdhclient error stream: %s)r`   r8   r9   )rG   errr)   r)   r*   dhcp_log_cb	  s   


r^  c                   @      e Zd ZdS )BrokenAzureDataSourceNr$   r   r   r)   r)   r)   r*   r`        r`  c                   @   r_  )NonAzureDataSourceNra  r)   r)   r)   r*   rc    rb  rc  c                   @   s   e Zd ZdddZddddddddddee dee d	ee d
ee dee deee	  dedee ddfddZ
defddZededd fddZ	d'dededefddZ			d(dedededef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)ovfwaNFr   passwordr   custom_datadisable_ssh_password_authpublic_keyspreprovisioned_vmpreprovisioned_vm_typer   rh  r   ri  rj  rk  rl  rm  r    c          	      C   s8   || _ || _|| _|| _|| _|pg | _|| _|| _d S r   rg  )	r   r   rh  r   ri  rj  rk  rl  rm  r)   r)   r*   r      s   

zOvfEnvXml.__init__c                 C   s   | j |j kS r   )__dict__)r   otherr)   r)   r*   __eq__5  s   zOvfEnvXml.__eq__ovf_env_xmlc              
   C   sp   zt |}W n t jy } z	d| }t||d}~ww |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   
NAMESPACESrc  rd  &_parse_linux_configuration_set_section _parse_platform_settings_section)clsrq  r   rF   	error_strinstancer)   r)   r*   
parse_text8  s   


zOvfEnvXml.parse_textrf  r   required	namespacec                 C   sl   | d||f tj}t|dkr"d| }t| |r t|d S t|dkr2td|t|f |d S )Nz./%s:%sr   #No ovf-env.xml configuration for %rr3   :Multiple configuration matches in ovf-exml.xml for %r (%d))findallrd  rr  r   r8   r9   r`  )r   noder   ry  rz  matchesrZ   r)   r)   r*   _findQ  s    

zOvfEnvXml._finddecode_base64
parse_boolc           
      C   s   | d| tj}t|dkr d| }t| |rt||S t|dkr0td|t|f |d j}	|	d u r;|}	|rK|	d urKt	d
|	 }	|rRt|	}	|	S )Nz./wa:r   r{  r3   r|  r   )r}  rd  rr  r   r8   r9   r`  r   rf   	b64decoder
  r=   r   translate_bool)
r   r~  r   ry  r  r  defaultr  rZ   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 )NProvisioningSectionTry  !LinuxProvisioningConfigurationSet
CustomDataF)r  ry  UserNameUserPasswordHostName DisableSshPasswordAuthentication)r  ry  )r  r  ri  r   rh  r   rj  _parse_ssh_section)r   r   provisioning_section
config_setr)   r)   r*   rs    s<   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 )	NPlatformSettingsSectionTr  PlatformSettingsPreprovisionedVmF)r  r  ry  PreprovisionedVMType)r  r  rl  rm  )r   r   platform_settings_sectionplatform_settingsr)   r)   r*   rt    s$   z*OvfEnvXml._parse_platform_settings_sectionc           	      C   s   g | _ | j|ddd}|d u rd S | j|ddd}|d u rd S |dtjD ]'}| j|ddd}| j|ddd}| j|dd	dd
}|||d}| j | q&d S )NSSHFr  
PublicKeysz./wa:PublicKeyFingerprintPathValuer   )r  ry  )r  r   r   )rk  r  r}  rd  rr  r  r  )	r   r  ssh_sectionpublic_keys_section
public_keyr  r   r   r  r)   r)   r*   r    s2   zOvfEnvXml._parse_ssh_section)rf  )FFN)r$   r   r   rr  r
   r   r   r   r	   rW  r   rp  classmethodrx  r  r  rs  rt  r  r)   r)   r)   r*   rd    sr    
	



$"rd  r   )Srf   rl   loggingry   r  r   r   r   rh   
contextlibr   r   r   r   r   r   typingr   r   r	   r
   r   r   	xml.etreer   xml.sax.saxutilsr   	cloudinitr   r   r   r   r   r   cloudinit.reportingr   cloudinit.settingsr   cloudinit.sources.azurer   	getLoggerr$   r8   DEFAULT_WIRESERVER_ENDPOINTr@   rT   r_   rk   r}   r   r#   r%   r   r,   rM   rW   r   r?   r`   rr   r   rv   r   r   rW  r   r   r   r   r   r   r   r   r   r   r"  rA  rZ  r\  r^  r`  rc  rd  r)   r)   r)   r*   <module>   s     
"
S

(

	6
"?  " f
		