U
    JhW                  
   @   s  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m	Z	m
Z
 d dlm  mZ d dlmZ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  d d	l!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Z0dZ1dZ2dZ3dddddZ4e5 Z6e7e8e9Z:edddgZ;G dd dej<Z=G dd  d e%j>Z?e@d!d"d#ZAdDeeeBef eeBef eCeCdd%d&d'ZDdEeeeBef eeBef eCeCe
eeCf d)d*d+ZEejFejGd,d-d.ZHed/d0d1ZIeee d2d3d4ZJeeBeeBef d5d6d7ZKeeBeBf eeBeBf eLd8d9d:ZMdFeeBef eBeBe	eB eeLeeBef f d;d<d=ZNdGeeBef e	eB e	eB dd>d?d@ZOeeeBef ee; dAdBdCZPdS )H    N)
namedtuple)AnyDictListOptionalTuple)	
data_typesevent_logger
exceptionshttpmessagessecret_managersystemutilversion)_enabled_services)_is_attached)UAConfig)ATTACH_FAIL_DATE_FORMAT)attachment_data_filemachine_id_file)serviceclient)get_user_or_root_log_file_pathz/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-attachz?/v1/contracts/{contract}/context/machines/{machine}/guest-token            )series_overridesseriescloudvariantEnableByDefaultServicenamer    c                   @   sR  e Zd Zejdejddejdejddejdejddejdejddejdejddejdejddejd	ejddejd
ejddejdejddejdejddejdejddejdejddejdejddejdejddgZdee ee ee ee ee ee ee ee ee ee ee ee ee ee dddZ	dS )CPUTypeDatacpuinfo_cpuF)Zrequiredcpuinfo_cpu_architecturecpuinfo_cpu_familycpuinfo_cpu_implementercpuinfo_cpu_partcpuinfo_cpu_revisioncpuinfo_cpu_variantcpuinfo_modelcpuinfo_model_namecpuinfo_steppingcpuinfo_vendor_id"sys_firmware_devicetree_base_modelsysinfo_modelsysinfo_typeNr$   r%   r&   r'   r(   r)   r*   r+   r,   r-   r.   r/   r0   r1   c                 C   sX   || _ || _|| _|| _|| _|| _|| _|| _|	| _|
| _	|| _
|| _|| _|| _d S )Nr2   )selfr$   r%   r&   r'   r(   r)   r*   r+   r,   r-   r.   r/   r0   r1    r4   3/usr/lib/python3/dist-packages/uaclient/contract.py__init__z   s    zCPUTypeData.__init__)NNNNNNNNNNNNNN)
__name__
__module____qualname__r   ZFieldZStringDataValueZfieldsr   strr6   r4   r4   r4   r5   r#   F   s                         5              r#   c                       sh  e Zd ZdZd*ee dd fd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eeef dddZd,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eeeed!d&d'Zd(d) Z  ZS )/UAContractClientZcontract_urlNcfgreturnc                    s   t  j|d t | _d S )Nr=   )superr6   mtfget_machine_token_filemachine_token_file)r3   r=   	__class__r4   r5   r6      s    zUAContractClient.__init__r   r   )Zretry_sleepsc                 C   s   |st | j}|  }|dd|i |  }| |d< ||d}t|}| j	t
||d}|jdkrvt n|jdkrt| |jdkrtjt
|j|jd	|j}	tj|	d
d |	dg D ]}
tj|
dd q|	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     urlcodebodymachineToken resourceTokenstoken)r   get_machine_idr=   rM   updateformat_get_activity_info	isoformat_support_old_machine_inforequest_urlAPI_V1_ADD_CONTRACT_MACHINErR   r
   ZAttachInvalidTokenError_raise_attach_forbidden_messageContractAPIErrorrS   	json_dictr   secrets
add_secretget)r3   contract_tokenZattachment_dt
machine_idrM   activity_inforL   backcompat_dataresponseresponse_jsonrW   r4   r4   r5   add_contract_machine   s<    
  




z%UAContractClient.add_contract_machine)r>   c                 C   sT   |   }| jt|d |d |d |d dd}|jdkrNtjt|j|jd|jS )	z=Requests list of entitlements available to this machine type.architecturer   kernelvirtrm   r   rn   ro   )query_paramsrO   rP   )r[   r^   API_V1_AVAILABLE_RESOURCESrR   r
   ra   rS   rb   )r3   rh   rj   r4   r4   r5   available_resources   s     	
z$UAContractClient.available_resources)rf   r>   c                 C   sN   |   }|dd|i | jt|d}|jdkrHtjt|j|jd|j	S )NrF   rG   rM   rO   rP   )
rM   rY   rZ   r^   API_V1_GET_CONTRACT_USING_TOKENrR   r
   ra   rS   rb   )r3   rf   rM   rj   r4   r4   r5   get_contract_using_token   s     
z)UAContractClient.get_contract_using_token)
cloud_typerL   c                C   sz   | j tj|d|d}|jdkr\|jdd}|rHt| tj	|dtj
t|j|jd|j}tj|dd |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.
        )rw   )rL   rO   messagerU   )	error_msgrP   contractToken)r^   ,API_V1_GET_CONTRACT_TOKEN_FOR_CLOUD_INSTANCErZ   rR   rb   re   LOGdebugr
   ZInvalidProImagera   rS   r   rc   rd   )r3   rw   rL   rj   msgrk   r4   r4   r5   %get_contract_token_for_cloud_instance   s*    



z6UAContractClient.get_contract_token_for_cloud_instance)machine_tokenresourcerg   r>   c           	      C   s   |st | j}|  }|dd|i tj||d}| j||d}|jdkrft	j
t|j|jd|jdr|jd |jd< |j}|dg D ]}tj|d	d
 q|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.

        @return: Dict of the JSON response containing entitlement accessInfo.
        rF   rG   )r   machinert   rO   rP   expiresrV   rW   rU   )r   rX   r=   rM   rY   rZ   "API_V1_GET_RESOURCE_MACHINE_ACCESSr^   rR   r
   ra   rS   re   rb   r   rc   rd   )	r3   r   r   rg   rM   rQ   rj   rk   rW   r4   r4   r5   get_resource_machine_access  s*     
z,UAContractClient.get_resource_machine_accessc                 C   s   | j j}| j jd}t| j}|  }tj	||d}| 
 }|dd	|i | j|||d}|jdkrtj||j|jd|jr| j j}|j|d< | 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.
        rT   Zcontractr   rF   rG   )rM   rL   rO   rP   rK   N)rC   contract_idr   re   r   rX   r=   r[   API_V1_UPDATE_ACTIVITY_TOKENrZ   rM   rY   r^   rR   r
   ra   rS   rb   write)r3   r   r   rg   Zrequest_datarQ   rM   rj   r4   r4   r5   update_activity_token;  s.     
  
z&UAContractClient.update_activity_token)magic_tokenr>   c                 C   s   |   }|dd|i | jt|d}|jdkr<t |jdkrNt |jdkrltj	t|j|j
d|j}dd	d
g}|D ]}tj||d q|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
        rF   rG   rt   rN     rO   rP   rW   userCoderz   rU   )rM   rY   rZ   r^   "API_V1_GET_MAGIC_ATTACH_TOKEN_INFOrR   r
   MagicAttachTokenErrorMagicAttachUnavailablera   rS   rb   r   rc   rd   re   )r3   r   rM   rj   rk   secret_fieldsfieldr4   r4   r5   get_magic_attach_token_infoc  s*     



z,UAContractClient.get_magic_attach_token_infoc                 C   sz   |   }| jt|dd}|jdkr*t |jdkrHtjt|j|jd|j}dddg}|D ]}t	j
||d	 q\|S )
z)Create a magic attach token for the user.POSTrM   methodr   rO   rP   rW   r   rz   rU   )rM   r^   API_V1_NEW_MAGIC_ATTACHrR   r
   r   ra   rS   rb   r   rc   rd   re   )r3   rM   rj   rk   r   r   r4   r4   r5   new_magic_attach_token  s&    


z'UAContractClient.new_magic_attach_token)r   c                 C   s   |   }|dd|i | jt|dd}|jdkr>t |jdkrPt |jdkrbt	 |jdkrtj
t|j|jd	d
S )z)Revoke a magic attach token for the user.rF   rG   ZDELETEr     rN   r   rO   rP   N)rM   rY   rZ   r^   API_V1_REVOKE_MAGIC_ATTACHrR   r
   Z MagicAttachTokenAlreadyActivatedr   r   ra   rS   )r3   r   rM   rj   r4   r4   r5   revoke_magic_attach_token  s&    



z*UAContractClient.revoke_magic_attach_token)r   r   rg   r>   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|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.
        rF   rG   r   GETrm   r   rn   ro   rp   )r   rM   rq   rO   rP   r   )r   rX   r=   rM   rY   rZ   API_V1_GET_CONTRACT_MACHINEr[   r^   rR   r
   ra   rS   re   rb   )r3   r   r   rg   rM   rQ   rh   rj   r4   r4   r5   get_contract_machine  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rtj||j
|jd|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
        rF   rG   rI   r   r   )rM   r   rL   rO   rP   r   )r   rX   r=   rM   rY   rZ   r[   r]   API_V1_UPDATE_CONTRACT_MACHINEr^   rR   r
   ra   rS   re   rb   )	r3   r   r   rg   rM   rL   ri   rQ   rj   r4   r4   r5   update_contract_machine  s6        
  z(UAContractClient.update_contract_machinec                 C   sv   |   }|dd|i tj||d}| j||dd}|jdkrRtjddn|jd	krptj||j|j	d
|j
S )a  Request guest token associated with this machine's contract
        @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: Unique machine id that was registered with the pro
            backend on attach.
        @return: Dict of the JSON response containing the guest token
        rF   rG   r   r   r   r   get_guest_token)Zfeature_namerO   rP   )rM   rY   rZ   API_V1_GET_GUEST_TOKENr^   rR   r
   Z FeatureNotSupportedOldTokenErrorra   rS   rb   )r3   r   r   rg   rM   rQ   rj   r4   r4   r5   r      s$    

z UAContractClient.get_guest_tokenc                 C   s   t  }t  jt  jt  jt  t  t 	 t
 t|j|j|j|j|j|j|j|j|j|j|j|j|j|jdjddd}t| jjrt| jj }t!" }| j#j$pt %| j| j#j&dd |D dd |D |r|j'( nd	d
}ni }||S )z9Return a dict of activity info data for contract requestsr2   F)Z	keep_none)distributionrn   r   rm   Zdesktopro   ZclientVersionZcpu_typec                 S   s   g | ]
}|j qS r4   )r"   .0servicer4   r4   r5   
<listcomp>H  s     z7UAContractClient._get_activity_info.<locals>.<listcomp>c                 S   s   i | ]}|j r|j|jqS r4   )Zvariant_enabledr"   Zvariant_namer   r4   r4   r5   
<dictcomp>I  s    z7UAContractClient._get_activity_info.<locals>.<dictcomp>N)Z
activityIDZactivityToken	resourcesZresourceVariantsrH   ))r   Zget_cpu_infoget_release_infor   Zget_kernel_infoZuname_releaser   Zget_dpkg_archZ
is_desktopZget_virt_typer   Zget_versionr#   r$   r%   r&   r'   r(   r)   r*   r+   r,   r-   r.   r/   r0   r1   Zto_dictr   r=   Zis_attachedr   enabled_servicesr   readrC   Zactivity_idrX   Zactivity_tokenZattached_atr\   )r3   ZcpuinfoZmachine_infor   Zattachment_datarh   r4   r4   r5   r[   #  sZ    
z#UAContractClient._get_activity_info)N)N)N)N)N)r7   r8   r9   Zcfg_url_base_attrr   r   r6   r   ZretrysocketZtimeoutrl   r   r:   r   rs   rv   r   r   r   r   r   r   r   r   r   r[   __classcell__r4   r4   rD   r5   r;      s\     * 
$ 
&( 
/ *#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.
    rK   rJ   rm   r   rn   r   ZLinux)r   rn   r   typerelease)rJ   rK   rm   os)re   r   r   r   )r   rh   r4   r4   r5   r]   ]  s    	r]   T)r=   past_entitlementsnew_entitlementsallow_enabler   r>   c                 C   sv  ddl m} d}g }g }|| D  ]}	z||	 }
W n tk
rJ   Y q Y nX g }z"t| ||	i |
||d\}}W n tjk
r } z*t| d}|	|	 t
d|	|
 W 5 d}~X Y q  tk
r
 } z0t| |	| |	|	 td|	|
 W 5 d}~X Y q X |r |r t|	 q t| t|dkrVtjd	d
 t||D dn|rrt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)r=   orig_access
new_accessr   r   Tz+Failed to process contract delta for %s: %rNz5Unexpected error processing contract delta for %s: %rc                 S   s*   g | ]"\}}|t jjt|t d fqS ))ry   Zlog_path)r   ZUNEXPECTED_ERRORrZ   r:   r   )r   r"   	exceptionr4   r4   r5   r     s   z.process_entitlements_delta.<locals>.<listcomp>)failed_servicesc                 S   s   g | ]}|t jfqS r4   )r   Z!E_ATTACH_FAILURE_DEFAULT_SERVICES)r   r"   r4   r4   r5   r     s   )uaclient.entitlementsr   KeyErrorprocess_entitlement_deltare   r
   ZUbuntuProErrorr|   r   appenderror	ExceptioneventZservice_processedZservices_failedlenZAttachFailureUnknownErrorzipZAttachFailureDefaultServices)r=   r   r   r   r   r   Zdelta_errorZunexpected_errorsr   r"   Znew_entitlementdeltasZservice_enableder4   r4   r5   process_entitlements_deltax  sf    







r   F)r=   r   r   r   r   r>   c              
   C   s   ddl m} |rt| t||}d}|r|di d}|sT|di d}|sft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 |
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 UbuntuProError: 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entitlementsobligationsuse_selectorrU   r=   r"   r    z3Skipping entitlement deltas for "%s". No such classNr   )r   r   apply_contract_overridesr   Zget_dict_deltasre   r
   Z InvalidContractDeltasServiceTypeEntitlementNotFoundErrorr|   r}   Zprocess_contract_deltas)r=   r   r   r   r   r   r   Zretr"   r    r   excr4   r4   r5   r     sP       
   r   )rj   r>   c                 C   s   | j d}|r|d }|d }|dkrR|d t}tj|||d ddnF|dkr|d t}tj|||d dd	n|d
krtj|dt d S )NinfoZ
contractIdreasonzno-longer-effectivetimez%m-%d-%Y)r   dateZcontract_expiry_dateznot-effective-yet)r   r   Zcontract_effective_dateznever-effective)r   )	rb   re   strftimer   r
   ZAttachForbiddenExpiredZAttachForbiddenNotYetZAttachForbiddenNeverZAttachExpiredToken)rj   r   r   r   r   r4   r4   r5   r`     s*    r`   r?   c           	      C   s   t | }| }|j}|d }|d d d }t| d}|j||d}|| tj	  |
di 
dt| }t| t| || dd	 d
S )zRequest contract refresh from ua-contracts service.

    :raise UbuntuProError: on failure to update contract or error processing
        contract deltas
    :raise ConnectivityError: On failure during a connection
    rT   ZmachineTokenInfoZcontractInfoidr?   )r   r   rJ   Fr   N)rA   rB   r   r   r;   r   r   r   rX   cache_clearre   r   r   )	r=   rC   Zorig_entitlementsZ
orig_tokenr   r   Zcontract_clientZresprg   r4   r4   r5   refresh.  s.    

 

 
r   r<   c                 C   s   t | }| }|dg S )zDQuery available resources from the contract server for this machine.r   )r;   rs   re   )r=   clientr   r4   r4   r5   get_available_resourcesO  s    r   )r=   rW   r>   c                 C   s   t | }||S )z/Query contract information for a specific token)r;   rv   )r=   rW   r   r4   r4   r5   get_contract_informationV  s    r   )override_selectorselector_valuesr>   c                 C   s<   d}|   D ]*\}}||f|  kr* dS |t| 7 }q|S )Nr   )itemsOVERRIDE_SELECTOR_WEIGHTS)r   r   Zoverride_weightselectorvaluer4   r4   r5   _get_override_weight\  s    r   )r   series_namerw   r    r>   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deepcopyre   r   )
r   r   rw   r    r   r   r   Zgeneral_overridesoverrideZweightr4   r4   r5   _select_overridesh  s"    

 
r   )r   r   r    r>   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RuntimeErrorrZ   r   r   r   re   r   sortedr   rY   )r   r   r    r   r   rw   _Zorig_entitlementr   Z_weightZoverrides_to_applykeyr   Zcurrentr4   r4   r5   r     s.    
   
r   )r=   r   r>   c              	   C   s   ddl m} g }| D ]\}}|di dd}z|| ||d}W n tjk
r`   Y qY nX |di di }|d}	|||	r| \}
}|
r|t	||d	 q|S )
Nr   r   r   r   rU   r   r   resourceToken)r"   r    )
r   r   r   re   r
   r   Z_should_enable_by_default
can_enabler   r!   )r=   r   r   Zenable_by_default_servicesZent_nameZ	ent_valuer    Zentr   r   r   r   r4   r4   r5   get_enabled_by_default_services  s(    

r   )T)FT)N)NN)Qr   Zloggingr   collectionsr   typingr   r   r   r   r   Zuaclient.files.machine_tokenfilesr   rA   Zuaclientr   r	   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   Zuaclient.files.state_filesr   r   Zuaclient.httpr   Zuaclient.logr   r_   r   r   rr   r   r{   r   ru   r   r   r   r   r   Zget_event_loggerr   Z	getLoggerZreplace_top_level_logger_namer7   r|   r!   Z
DataObjectr#   ZUAServiceClientr;   r   r]   r:   boolr   r   ZHTTPResponseZNamedMessager`   r   r   r   intr   r   r   r   r4   r4   r4   r5   <module>   s   , W   C  

^  


@!
 
 
  
2 
