U
    2dbz                  
   @   s  d dl Z d dlZd dlZd dlZd dlmZmZmZmZm	Z	 d dl
mZmZ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 d dlmZmZmZ d dlmZ d d	lm Z  d
Z!dZ"dZ#dZ$dZ%dZ&dZ'dZ(dZ)dZ*dZ+dddddZ,e- Z.e/e0e1Z2ej3G dd dej4Z5G dd de j6Z7e8dddZ9d>eee:ef ee:ef e;e;ddd d!Z<d?eee:ef ee:ef e;e;e	ee;f d#d$d%Z=ej>ej?d&d'd(Z@d)d* ZAeee d+d,d-ZBee:ee:ef d.d/d0ZCee;d+d1d2ZDee:e:f ee:e:f eEd3d4d5ZFd@ee:ef e:e:ee: eeEee:ef f d6d7d8ZGdAee:ef ee: ee: dd9d:d;ZHee	e5eEf d+d<d=ZIdS )B    N)AnyDictListOptionalTuple)cloudsevent_logger
exceptionshttpmessagessystemutilversion)_enabled_services)_is_attached)UAConfig)ATTACH_FAIL_DATE_FORMAT!CONTRACT_EXPIRY_GRACE_PERIOD_DAYSCONTRACT_EXPIRY_PENDING_DAYS)attachment_data_file)serviceclientz/v1/context/machines/tokenz3/v1/contracts/{contract}/context/machines/{machine}z/v1/resourcesz3/v1/resources/{resource}/context/machines/{machine}z/v1/clouds/{cloud_type}/tokenz3/v1/contracts/{contract}/machine-activity/{machine}z/v1/contractz/v1/magic-attach            )series_overridesseriescloudvariantc                   @   s    e Zd ZdZdZdZdZdZdS )ContractExpiryStatusr   r   r   r   r   N)__name__
__module____qualname__NONEACTIVEACTIVE_EXPIRED_SOONEXPIRED_GRACE_PERIODEXPIRED r(   r(   3/usr/lib/python3/dist-packages/uaclient/contract.pyr   >   s
   r   c                   @   s0  e Zd ZdZejejdddgdd&ddZe	e
ef dd	d
Ze
e	e
ef dddZejejdddgdejdddZd'e
e
ee
 ee	e
ef dddZdd Ze
e	e
ef dddZe	e
ef dddZe
dddZd(e
e
ee
 e	e
ef dd d!Zd)e
e
ee
 e	dd"d#Zd$d% ZdS )*UAContractClientZcontract_urlr   r   )Zretry_sleepsNc           
      C   s   |st | j}|  }|dd|i |  }| |d< ||d}t|}| j	t
||d}|jdkrvt n(|jdkrt|}	tj|	j|	j|	jd|jd	krtt
|j|j|jS )
a}  Requests machine attach to the provided machine_id.

        @param contract_token: Token string providing authentication to
            ContractBearer service endpoint.
        @param machine_id: Optional unique system machine id. When absent,
            contents of /etc/machine-id will be used.

        @return: Dict of the JSON response containing the machine-token.
        Authorization	Bearer {}lastAttachment	machineIdactivityInfo)dataheaders  i  )msgmsg_codeadditional_info   )r   get_machine_idcfgr2   updateformat_get_activity_info	isoformat_support_old_machine_inforequest_urlAPI_V1_ADD_CONTRACT_MACHINEcoder	   ZAttachInvalidTokenError _create_attach_forbidden_messageUserFacingErrorr4   namer6   ContractAPIErrorbody	json_dict)
selfcontract_tokenZattachment_dt
machine_idr2   activity_infor1   backcompat_dataresponser4   r(   r(   r)   add_contract_machineJ   s:    
  



  z%UAContractClient.add_contract_machine)returnc                 C   sR   |   }| jt|d |d |d |d dd}|jdkrLtt|j|j|jS )z=Requests list of entitlements available to this machine type.architecturer   kernelvirtrP   r   rQ   rR   )query_paramsr7   )r<   r?   API_V1_AVAILABLE_RESOURCESrA   r	   rE   rF   rG   )rH   rK   rM   r(   r(   r)   available_resourcess   s     	
  z$UAContractClient.available_resources)rI   rO   c                 C   sL   |   }|dd|i | jt|d}|jdkrFtt|j|j|j	S )Nr+   r,   r2   r7   )
r2   r:   r;   r?   API_V1_GET_CONTRACT_USING_TOKENrA   r	   rE   rF   rG   )rH   rI   r2   rM   r(   r(   r)   get_contract_using_token   s     
  z)UAContractClient.get_contract_using_token)instancec                C   st   | j tj|jd|jd}|jdkr^|jdd}|rLt	| t
j|dt
t|j|j| jd|j |jS )zRequests contract token for auto-attach images for Pro clouds.

        @param instance: AutoAttachCloudInstance for the cloud.

        @return: Dict of the JSON response containing the contract-token.
        )
cloud_type)r1   r7   message )Z	error_msgzcontract-token)r?   ,API_V1_GET_CONTRACT_TOKEN_FOR_CLOUD_INSTANCEr;   r[   Zidentity_docrA   rG   getLOGdebugr	   ZInvalidProImagerE   rF   r9   write_cache)rH   rZ   rM   r4   r(   r(   r)   %get_contract_token_for_cloud_instance   s$    


z6UAContractClient.get_contract_token_for_cloud_instanceT)machine_tokenresourcerJ   	save_filerO   c                 C   s   |st | j}|  }|dd|i tj||d}| j||d}|jdkrdt	
t|j|j|jdr|jd |jd< |r| jd||j |jS )a  Requests machine access context for a given resource

        @param machine_token: The authentication token needed to talk to
            this contract service endpoint.
        @param resource: Entitlement name.
        @param machine_id: Optional unique system machine id. When absent,
            contents of /etc/machine-id will be used.
        @save_file: If the machine access should be saved on the user machine

        @return: Dict of the JSON response containing entitlement accessInfo.
        r+   r,   )re   machinerW   r7   expireszmachine-access-{})r   r8   r9   r2   r:   r;   "API_V1_GET_RESOURCE_MACHINE_ACCESSr?   rA   r	   rE   rF   r_   rG   rb   )rH   rd   re   rJ   rf   r2   urlrM   r(   r(   r)   get_resource_machine_access   s.     
 z,UAContractClient.get_resource_machine_accessc                 C   s   | j jj}| j jd}t| j }|  }tj	||d}| 
 }|dd	|i | j|||d}|jdkrt||j|j|jr| j j}|j|d< | j j| dS )	zReport current activity token and enabled services.

        This will report to the contracts backend all the current
        enabled services in the system.
        machineTokenZcontractrg   r+   r,   )r2   r1   r7   r0   N)r9   machine_token_filecontract_idrd   r_   r   r8   r<   API_V1_UPDATE_ACTIVITY_TOKENr;   r2   r:   r?   rA   r	   rE   rF   rG   write)rH   ro   rd   rJ   Zrequest_datarj   r2   rM   r(   r(   r)   update_activity_token   s*    
 
  
z&UAContractClient.update_activity_token)magic_tokenrO   c              
   C   s   |   }|dd|i z| jt|d}W n: tjk
rh } ztt	| t
 W 5 d}~X Y nX |jdkr|t |jdkrt |jdkrtt|j|j|jS )zRequest magic attach token info.

        When the magic token is registered, it will contain new fields
        that will allow us to know that the attach process can proceed
        r+   r,   rW   Nr3     r7   )r2   r:   r;   r?   "API_V1_GET_MAGIC_ATTACH_TOKEN_INFOr	   UrlErrorr`   	exceptionstrConnectivityErrorrA   MagicAttachTokenErrorMagicAttachUnavailablerE   rF   rG   rH   rs   r2   rM   er(   r(   r)   get_magic_attach_token_info   s*     



z,UAContractClient.get_magic_attach_token_infoc              
   C   s   |   }z| jt|dd}W n: tjk
rV } ztt| t W 5 d}~X Y nX |j	dkrjt
 |j	dkrtt|j	|j|jS )z)Create a magic attach token for the user.POSTr2   methodNrt   r7   )r2   r?   API_V1_NEW_MAGIC_ATTACHr	   rv   r`   rw   rx   ry   rA   r{   rE   rF   rG   )rH   r2   rM   r}   r(   r(   r)   new_magic_attach_token  s&    


  z'UAContractClient.new_magic_attach_token)rs   c              
   C   s   |   }|dd|i z| jt|dd}W n: tjk
rj } ztt	| t
 W 5 d}~X Y nX |jdkr~t |jdkrt |jdkrt |jd	krtt|j|jdS )
z)Revoke a magic attach token for the user.r+   r,   ZDELETEr   Ni  r3   rt   r7   )r2   r:   r;   r?   API_V1_REVOKE_MAGIC_ATTACHr	   rv   r`   rw   rx   ry   rA   Z MagicAttachTokenAlreadyActivatedrz   r{   rE   rF   r|   r(   r(   r)   revoke_magic_attach_token0  s.    




  z*UAContractClient.revoke_magic_attach_token)rd   ro   rJ   rO   c              	   C   s   |st | j}|  }|dd|i tj||d}|  }| j|d||d |d |d |d d	d
}|j	dkrt
||j	|j|jdr|jd |jd< |jS )a|  Get the updated machine token from the contract server.

        @param machine_token: The machine token needed to talk to
            this contract service endpoint.
        @param contract_id: Unique contract id provided by contract service
        @param machine_id: Optional unique system machine id. When absent,
            contents of /etc/machine-id will be used.
        r+   r,   rm   ZGETrP   r   rQ   rR   rS   )r   r2   rT   r7   rh   )r   r8   r9   r2   r:   r;   API_V1_GET_CONTRACT_MACHINEr<   r?   rA   r	   rE   rF   r_   rG   )rH   rd   ro   rJ   r2   rj   rK   rM   r(   r(   r)   get_contract_machineI  s8    
  z%UAContractClient.get_contract_machinec           	      C   s   |st | j}|  }|dd|i ||  d}t|}tj||d}| j	||d|d}|j
dkr~t||j
|j|jdr|jd |jd< |jS )	a  Request machine token refresh from contract server.

        @param machine_token: The machine token needed to talk to
            this contract service endpoint.
        @param contract_id: Unique contract id provided by contract service.
        @param machine_id: Optional unique system machine id. When absent,
            contents of /etc/machine-id will be used.

        @return: Dict of the JSON response containing refreshed machine-token
        r+   r,   r.   rm   r   )r2   r   r1   r7   rh   )r   r8   r9   r2   r:   r;   r<   r>   API_V1_UPDATE_CONTRACT_MACHINEr?   rA   r	   rE   rF   r_   rG   )	rH   rd   ro   rJ   r2   r1   rL   rj   rM   r(   r(   r)   update_contract_machinet  s6        
  z(UAContractClient.update_contract_machinec                 C   s   t  jt  jt  jt  t  t  t	
 d}t| jjrt| jj}t }| jjjpjt | j| jjjdd |D |r|j ndd}ni }||S )z9Return a dict of activity info data for contract requests)distributionrQ   r   rP   ZdesktoprR   ZclientVersionc                 S   s   g | ]
}|j qS r(   )rD   ).0servicer(   r(   r)   
<listcomp>  s     z7UAContractClient._get_activity_info.<locals>.<listcomp>N)Z
activityIDZactivityToken	resourcesr-   )r   get_release_infor   Zget_kernel_infoZuname_releaser   Zget_dpkg_archZ
is_desktopZget_virt_typer   Zget_versionr   r9   is_attachedr   enabled_servicesr   readrn   Zactivity_idr8   Zactivity_tokenZattached_atr=   )rH   Zmachine_infor   Zattachment_datarK   r(   r(   r)   r<     s.    



z#UAContractClient._get_activity_info)N)NT)N)N)r    r!   r"   Zcfg_url_base_attrr   ZretrysocketZtimeoutrN   r   rx   r   rV   rY   r   ZAutoAttachCloudInstancerc   r   boolrk   rr   r~   r   r   r   r   r<   r(   r(   r(   r)   r*   G   sJ    (!  
(& 
/ (r*   )request_bodyc              	   C   sJ   |  di }|  d|| d| d| d| ddt jdd	S )
a?  
    Transforms a request_body that has the new activity_info into a body that
    includes both old and new forms of machineInfo/activityInfo

    This is necessary because there may be old ua-airgapped contract
    servers deployed that we need to support.
    This function is used for attach and refresh calls.
    r0   r/   rP   r   rQ   r   ZLinux)r   rQ   r   typerelease)r/   r0   rP   os)r_   r   r   r   )r   rK   r(   r(   r)   r>     s    	r>   T)r9   past_entitlementsnew_entitlementsallow_enabler   rO   c                 C   sd  ddl m} d}d}g }|| D ]}	z||	 }
W n tk
rH   Y q Y nX g }z"t| ||	i |
||d\}}W n tjk
r   d}||	 t	  t
dj|	|
d W 5 Q R X Y q  tk
r   d}||	 t	  t
dj|	|
d W 5 Q R X Y q X |r |r t|	 q t| |rDtjd	d
 |D dn|r`tjdd
 |D ddS )a  Iterate over all entitlements in new_entitlement and apply any delta
    found according to past_entitlements.

    :param cfg: UAConfig instance
    :param past_entitlements: dict containing the last valid information
        regarding service entitlements.
    :param new_entitlements: dict containing the current information regarding
        service entitlements.
    :param allow_enable: Boolean set True if allowed to perform the enable
        operation. When False, a message will be logged to inform the user
        about the recommended enabled service.
    :param series_overrides: Boolean set True if series overrides should be
        applied to the new_access dict.
    r   )entitlements_enable_orderF)r9   orig_access
new_accessr   r   Tz4Failed to process contract delta for {name}: {delta})rD   Zdeltaz>Unexpected error processing contract delta for {name}: {delta}c                 S   s   g | ]}|t jfqS r(   )r   ZUNEXPECTED_ERRORr   rD   r(   r(   r)   r     s    z.process_entitlements_delta.<locals>.<listcomp>)failed_servicesc                 S   s   g | ]}|t jfqS r(   )r   ZATTACH_FAILURE_DEFAULT_SERVICESr   r(   r(   r)   r   #  s   N)uaclient.entitlementsr   KeyErrorprocess_entitlement_deltar_   r	   rC   appendr   Zdisable_log_to_consoler`   errorr;   	Exceptionrw   eventZservice_processedZservices_failedZAttachFailureUnknownErrorZAttachFailureDefaultServices)r9   r   r   r   r   r   Zdelta_errorZunexpected_errorr   rD   new_entitlementdeltasZservice_enabledr(   r(   r)   process_entitlements_delta  sj    



 

 
r   F)r9   r   r   r   r   rO   c              
   C   s  ddl m} |rt| t||}d}|r|di d}|sT|di d}|sztjj||d}	t	j
|	j|	jd|di d	i d
d}
z|| ||
d}W n4 t	jk
r } ztd| |W 5 d}~X Y nX || |d}|j|||d}||fS )a-  Process a entitlement access dictionary deltas if they exist.

    :param cfg: UAConfig instance
    :param orig_access: Dict with original entitlement access details before
        contract refresh deltas
    :param new_access: Dict with updated entitlement access details after
        contract refresh
    :param allow_enable: Boolean set True if allowed to perform the enable
        operation. When False, a message will be logged to inform the user
        about the recommended enabled service.
    :param series_overrides: Boolean set True if series overrides should be
        applied to the new_access dict.

    :raise UserFacingError: on failure to process deltas.
    :return: A tuple containing a dict of processed deltas and a
             boolean indicating if the service was fully processed
    r   )entitlement_factoryFentitlementr   )Zorignew)r4   r5   entitlementsZobligationsZuse_selectorr]   )r9   rD   r   z3Skipping entitlement deltas for "%s". No such classN)r9   Z
assume_yesr   )r   r   apply_contract_overridesr   get_dict_deltasr_   r   Z$INVALID_CONTRACT_DELTAS_SERVICE_TYPEr;   r	   rC   r4   rD   ZEntitlementNotFoundErrorr`   ra   Zprocess_contract_deltas)r9   r   r   r   r   r   r   ZretrD   r4   r   Zent_clsexcr   r(   r(   r)   r   *  sL          r   )rM   rO   c                 C   s   t j}| jd}|r|d }|d }d }|dkrl|d t}|d d|d}t jj||d}||_nX|d	kr|d t}|d d|d
}t j	j||d}||_n|dkrt j
j|d}|rt jj|jd}|j|_|j|_|S )NinfoZ
contractIdreasonzno-longer-effectivetimez%m-%d-%Y)Zcontract_expiry_datero   )ro   dateznot-effective-yet)Zcontract_effective_datero   znever-effective)ro   )r   )r   ZATTACH_EXPIRED_TOKENrG   r_   strftimer   ZATTACH_FORBIDDEN_EXPIREDr;   r6   ZATTACH_FORBIDDEN_NOT_YETZATTACH_FORBIDDEN_NEVERZATTACH_FORBIDDENr4   rD   )rM   r4   r   ro   r   Z
reason_msgr   r6   r(   r(   r)   rB   g  sF      rB   c                 C   s   | j j}| j}|d }|d d d }t| }|j||d}| j | tj  |	di 	dt| }| 
d| t| || j jdd	 d
S )a  Request contract refresh from ua-contracts service.

    :param cfg: Instance of UAConfig for this machine.

    :raise UserFacingError: on failure to update contract or error processing
        contract deltas
    :raise UrlError: On failure during a connection
    rl   machineTokenInfocontractInfoid)rd   ro   r/   z
machine-idFr   N)rn   r   rd   r*   r   rq   r   r8   cache_clearr_   rb   r   )r9   orig_entitlements
orig_tokenrd   ro   contract_clientresprJ   r(   r(   r)   refresh  s,    	 
 r   )r9   rO   c                 C   s   t | }| }|dg S )zDQuery available resources from the contract server for this machine.r   )r*   rV   r_   )r9   clientr   r(   r(   r)   get_available_resources  s    r   )r9   tokenrO   c                 C   s   t | }||S )z/Query contract information for a specific token)r*   rY   )r9   r   r   r(   r(   r)   get_contract_information  s    r   c                 C   s   | j }| jj}|dd}|di di dd }|s>dS t| }|||}|di di dd }|rv|n| jj}| jj|krdS | j|}	t|		 D ]&\}
}t
||
i |}|r dS qdS )	Nrl   r]   r   r   r   FZeffectiveToT)rd   rn   r   r_   r*   r   Zcontract_expiry_datetimeZget_entitlements_from_tokensorteditemsr   r   )r9   r   r   rd   ro   r   r   Zresp_expiryZ
new_expiryZcurr_entitlementsrD   r   r   r(   r(   r)   is_contract_changed  sP        
 r   )override_selectorselector_valuesrO   c                 C   s<   d}|   D ]*\}}||f|  kr* dS |t| 7 }q|S )Nr   )r   OVERRIDE_SELECTOR_WEIGHTS)r   r   Zoverride_weightselectorvaluer(   r(   r)   _get_override_weight  s    r   )r   series_namer[   r   rO   c           
      C   sz   i }||d}|r||d< |  di  |i }|r>||td < t| dg }|D ] }t| d|}	|	rT|||	< qT|S )N)r   r   r   r   r   	overridesr   )popr   copydeepcopyr_   r   )
r   r   r[   r   r   r   r   Zgeneral_overridesoverrideZweightr(   r(   r)   _select_overrides  s&    
 
r   )r   r   r   rO   c                 C   s   ddl m} tt| td| kgs0td| |dkrBt j	n|}| \}}| 
di }t||||}t| D ]J\}	}
|
 D ]8\}}| d 
|}t|tr|| q|| d |< qqvdS )a  Apply series-specific overrides to an entitlement dict.

    This function mutates orig_access dict by applying any series-overrides to
    the top-level keys under 'entitlement'. The series-overrides are sparse
    and intended to supplement existing top-level dict values. So, sub-keys
    under the top-level directives, obligations and affordance sub-key values
    will be preserved if unspecified in series-overrides.

    To more clearly indicate that orig_access in memory has already had
    the overrides applied, the 'series' key is also removed from the
    orig_access dict.

    :param orig_access: Dict with original entitlement access details
    r   )get_cloud_typer   z?Expected entitlement access dict. Missing "entitlement" key: {}N)Zuaclient.clouds.identityr   all
isinstancedictRuntimeErrorr;   r   r   r   r_   r   r   r   r:   )r   r   r   r   r   r[   _Zorig_entitlementr   Z_weightZoverrides_to_applykeyr   Zcurrentr(   r(   r)   r     s.    
   
r   c                 C   s   t | jstjdfS t}t}| jj}|dkrBt	d tj
| fS d|  krV|krdn n
tj|fS | |  krzdk rn n
tj|fS || k rtj
|fS tj|fS )z/Return a tuple [ContractExpiryStatus, num_days]r   Nz:contract effectiveTo date is null - assuming it is expired)r   r   r   r#   r   r   rn   Zcontract_remaining_daysr`   Zwarningr'   r%   r&   r$   )r9   Zgrace_periodZpending_expiryZremaining_daysr(   r(   r)   get_contract_expiry_statusA  s"    





r   )T)FT)N)NN)Jr   enumZloggingr   typingr   r   r   r   r   Zuaclientr   r   r	   r
   r   r   r   r   Z-uaclient.api.u.pro.status.enabled_services.v1r   Z(uaclient.api.u.pro.status.is_attached.v1r   Zuaclient.configr   Zuaclient.defaultsr   r   r   Zuaclient.files.state_filesr   Zuaclient.httpr   r@   r   r   rU   ri   r^   rp   rX   ru   r   r   r   Zget_event_loggerr   Z	getLoggerZreplace_top_level_logger_namer    r`   uniqueEnumr   ZUAServiceClientr*   r   r>   rx   r   r   r   ZHTTPResponseZNamedMessagerB   r   r   r   r   intr   r   r   r   r(   r(   r(   r)   <module>   s   (
  y  

U  


>+"'
 
 
  
2
