o
    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?d!e@fd"d#ZA	$dKd%ed&eeBef d'eeBef d(eCd)eCd*dfd+d,ZD	-	$dLd%ed.eeBef d/eeBef d(eCd)eCd*e
eeCf fd0d1ZEd2ejFd*ejGfd3d4ZHd%efd5d6ZId%ed*ee fd7d8ZJd%ed9eBd*eeBef fd:d;ZKd<eeBeBf d=eeBeBf d*eLfd>d?ZM	dMd@eeBef dAeBdBeBde	eB d*eeLeeBef f f
dCdDZN		dNd.eeBef dEe	eB de	eB d*dfdFdGZOd%edHeeBef d*ee; fdIdJZPdS )O    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                   @   s  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dee dee dee dee dee dee d	ee d
ee dee dee dee dee dee dee fddZ	dS )CPUTypeDatacpuinfo_cpuF)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_typeNc                 C   sX   || _ || _|| _|| _|| _|| _|| _|| _|	| _|
| _	|| _
|| _|| _|| _d S Nr$   r&   r'   r(   r)   r*   r+   r,   r-   r.   r/   r0   r1   r2   )selfr$   r&   r'   r(   r)   r*   r+   r,   r-   r.   r/   r0   r1   r2    r6   3/usr/lib/python3/dist-packages/uaclient/contract.py__init__z   s   
zCPUTypeData.__init__)NNNNNNNNNNNNNN)
__name__
__module____qualname__r   FieldStringDataValuefieldsr   strr8   r6   r6   r6   r7   r#   F   s    5	
r#   c                       s  e Zd ZdZ	d+dee ddf fddZeje	j
g dd	d+d	d
Zdeeef fddZdedeeef fddZeje	j
g dddedeeef fddZ	d+dededee deeef fddZdd Zdedeeef fddZdeeef fddZdefd d!Z	d+ded"edee deeef fd#d$Z	d+ded"edee defd%d&Zded"ededefd'd(Zd)d* Z  ZS ),UAContractClientcontract_urlNcfgreturnc                    s   t  j|d t | _d S )NrB   )superr8   mtfget_machine_token_filemachine_token_file)r5   rB   	__class__r6   r7   r8      s   zUAContractClient.__init__)r   r   r   )retry_sleepsc                 C   s   |st | j}|  }|dd|i |  }| |d< ||d}t|}| j	t
||d}|jdkr:t |jdkrCt| |jdkrRtjt
|j|jd	|j}	tj|	d
d |	dg D ]}
tj|
dd qe|	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_idrB   rS   updateformat_get_activity_info	isoformat_support_old_machine_inforequest_urlAPI_V1_ADD_CONTRACT_MACHINErX   r
   AttachInvalidTokenError_raise_attach_forbidden_messageContractAPIErrorrY   	json_dictr   secrets
add_secretget)r5   contract_tokenattachment_dt
machine_idrS   activity_inforR   backcompat_dataresponseresponse_jsonr]   r6   r6   r7   add_contract_machine   s8   




z%UAContractClient.add_contract_machinec                 C   sT   |   }| jt|d |d |d |d dd}|jdkr'tjt|j|jd|jS )	z=Requests list of entitlements available to this machine type.architecturer   kernelvirtru   r   rv   rw   )query_paramsrU   rV   )ra   rd   API_V1_AVAILABLE_RESOURCESrX   r
   rh   rY   ri   )r5   rp   rr   r6   r6   r7   available_resources   s    
	z$UAContractClient.available_resourcesrm   c                 C   sN   |   }|dd|i | jt|d}|jdkr$tjt|j|jd|j	S )NrL   rM   rS   rU   rV   )
rS   r_   r`   rd   API_V1_GET_CONTRACT_USING_TOKENrX   r
   rh   rY   ri   )r5   rm   rS   rr   r6   r6   r7   get_contract_using_token   s   
z)UAContractClient.get_contract_using_token
cloud_typerR   c                C   sz   | j tj|d|d}|jdkr.|jdd}|r$t| 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.
        )r   )rR   rU   messager[   )	error_msgrV   contractToken)rd   ,API_V1_GET_CONTRACT_TOKEN_FOR_CLOUD_INSTANCEr`   rX   ri   rl   LOGdebugr
   InvalidProImagerh   rY   r   rj   rk   )r5   r   rR   rr   msgrs   r6   r6   r7   %get_contract_token_for_cloud_instance   s*   



z6UAContractClient.get_contract_token_for_cloud_instancemachine_tokenresourcero   c           	      C   s   |st | j}|  }|dd|i tj||d}| j||d}|jdkr3t	j
t|j|jd|jdrA|jd |jd< |j}|dg D ]}tj|d	d
 qJ|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.
        rL   rM   )r   machiner|   rU   rV   expiresr\   r]   r[   )r   r^   rB   rS   r_   r`   "API_V1_GET_RESOURCE_MACHINE_ACCESSrd   rX   r
   rh   rY   rl   ri   r   rj   rk   )	r5   r   r   ro   rS   rW   rr   rs   r]   r6   r6   r7   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rAtj||j|jd|jrU| j j}|j|d< | j | d	S d	S )
zReport current activity token and enabled services.

        This will report to the contracts backend all the current
        enabled services in the system.
        rZ   contractr   rL   rM   )rS   rR   rU   rV   rQ   N)rH   contract_idr   rl   r   r^   rB   ra   API_V1_UPDATE_ACTIVITY_TOKENr`   rS   r_   rd   rX   r
   rh   rY   ri   write)r5   r   r   ro   request_datarW   rS   rr   r6   r6   r7   update_activity_token;  s*   


z&UAContractClient.update_activity_tokenmagic_tokenc                 C   s   |   }|dd|i | jt|d}|jdkrt |jdkr't |jdkr6tj	t|j|j
d|j}g d}|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
        rL   rM   r|   rT     rU   rV   r]   userCoder   r[   )rS   r_   r`   rd   "API_V1_GET_MAGIC_ATTACH_TOKEN_INFOrX   r
   MagicAttachTokenErrorMagicAttachUnavailablerh   rY   ri   r   rj   rk   rl   )r5   r   rS   rr   rs   secret_fieldsfieldr6   r6   r7   get_magic_attach_token_infoc  s(   


z,UAContractClient.get_magic_attach_token_infoc                 C   sx   |   }| jt|dd}|jdkrt |jdkr$tjt|j|jd|j}g d}|D ]}t	j
||d q-|S )z)Create a magic attach token for the user.POSTrS   methodr   rU   rV   r   r[   )rS   rd   API_V1_NEW_MAGIC_ATTACHrX   r
   r   rh   rY   ri   r   rj   rk   rl   )r5   rS   rr   rs   r   r   r6   r6   r7   new_magic_attach_token  s&   

z'UAContractClient.new_magic_attach_tokenc                 C   s   |   }|dd|i | jt|dd}|jdkrt |jdkr(t |jdkr1t	 |jdkr@tj
t|j|jd	d
S )z)Revoke a magic attach token for the user.rL   rM   DELETEr     rT   r   rU   rV   N)rS   r_   r`   rd   API_V1_REVOKE_MAGIC_ATTACHrX   r
    MagicAttachTokenAlreadyActivatedr   r   rh   rY   )r5   r   rS   rr   r6   r6   r7   revoke_magic_attach_token  s(   



z*UAContractClient.revoke_magic_attach_tokenr   c              	   C   s   |st | j}|  }|dd|i tj||d}|  }| j|d||d |d |d |d d	d
}|j	dkrFt
j||j	|jd|jdrT|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.
        rL   rM   r   GETru   r   rv   rw   rx   )r   rS   ry   rU   rV   r   )r   r^   rB   rS   r_   r`   API_V1_GET_CONTRACT_MACHINEra   rd   rX   r
   rh   rY   rl   ri   )r5   r   r   ro   rS   rW   rp   rr   r6   r6   r7   get_contract_machine  s4   

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|jd	rN|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
        rL   rM   rO   r   r   )rS   r   rR   rU   rV   r   )r   r^   rB   rS   r_   r`   ra   rc   API_V1_UPDATE_CONTRACT_MACHINErd   rX   r
   rh   rY   rl   ri   )	r5   r   r   ro   rS   rR   rq   rW   rr   r6   r6   r7   update_contract_machine  s*   

z(UAContractClient.update_contract_machinec                 C   st   |   }|dd|i tj||d}| j||dd}|jdkr(tjdd|jd	kr7t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
        rL   rM   r   r   r   r   get_guest_token)feature_namerU   rV   )rS   r_   r`   API_V1_GET_GUEST_TOKENrd   rX   r
    FeatureNotSupportedOldTokenErrorrh   rY   ri   )r5   r   r   ro   rS   rW   rr   r6   r6   r7   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rvt| jj }t!" }| j#j$p[t %| j| j#j&dd |D dd |D |rq|j'( nd	d
}ni }i ||S )z9Return a dict of activity info data for contract requestsr4   F)	keep_none)distributionrv   r   ru   desktoprw   clientVersioncpu_typec                 S   s   g | ]}|j qS r6   )r"   .0servicer6   r6   r7   
<listcomp>H  s    z7UAContractClient._get_activity_info.<locals>.<listcomp>c                 S   s   i | ]
}|j r|j|jqS r6   )variant_enabledr"   variant_namer   r6   r6   r7   
<dictcomp>I  s    z7UAContractClient._get_activity_info.<locals>.<dictcomp>N)
activityIDactivityToken	resourcesresourceVariantsrN   ))r   get_cpu_infoget_release_infor   get_kernel_infouname_releaser   get_dpkg_arch
is_desktopget_virt_typer   get_versionr#   r$   r&   r'   r(   r)   r*   r+   r,   r-   r.   r/   r0   r1   r2   to_dictr   rB   is_attachedr   enabled_servicesr   readrH   activity_idr^   activity_tokenattached_atrb   )r5   cpuinfomachine_infor   attachment_datarp   r6   r6   r7   ra   #  s^   
z#UAContractClient._get_activity_infor3   )r9   r:   r;   cfg_url_base_attrr   r   r8   r   retrysockettimeoutrt   r   r?   r   r{   r~   r   r   r   r   r   r   r   r   r   ra   __classcell__r6   r6   rI   r7   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.
    rQ   rP   ru   r   rv   r   Linux)r   rv   r   typerelease)rP   rQ   ru   os)rl   r   r   r   )r   rp   r6   r6   r7   rc   ]  s   	rc   TrB   past_entitlementsnew_entitlementsallow_enabler   rC   c                 C   sj  ddl m} d}g }g }|| D ]|}	z||	 }
W n	 ty!   Y qw g }zt| ||	i |
||d\}}W nM tjy[ } zt| d}|	|	 t
d|	|
 W Y d}~qd}~w ty } zt| |	| |	|	 td|	|
 W Y d}~qd}~ww |r|rt|	 qt| t|dkrtjd	d
 t||D d|rt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)rB   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 ))r   log_path)r   UNEXPECTED_ERRORr`   r?   r   )r   r"   	exceptionr6   r6   r7   r     s    z.process_entitlements_delta.<locals>.<listcomp>)failed_servicesc                 S   s   g | ]}|t jfqS r6   )r   !E_ATTACH_FAILURE_DEFAULT_SERVICES)r   r"   r6   r6   r7   r     s    )uaclient.entitlementsr   KeyErrorprocess_entitlement_deltarl   r
   UbuntuProErrorr   r   appenderror	Exceptioneventservice_processedservices_failedlenAttachFailureUnknownErrorzipAttachFailureDefaultServices)rB   r   r   r   r   r   delta_errorunexpected_errorsr   r"   new_entitlementdeltasservice_enableder6   r6   r7   process_entitlements_deltax  sr   







r   Fr   r   c              
   C   s   ddl m} |rt| t||}d}|rh|di d}|s*|di d}|s3tj||d|di di d	d
}	z	|| ||	d}
W n tjy_ } zt	
d| |d}~ww |
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   )orignewentitlementsobligationsuse_selectorr[   rB   r"   r    z3Skipping entitlement deltas for "%s". No such classNr   )r   r   apply_contract_overridesr   get_dict_deltasrl   r
    InvalidContractDeltasServiceTypeEntitlementNotFoundErrorr   r   process_contract_deltas)rB   r   r   r   r   r   r   retr"   r    r  excr6   r6   r7   r     sD   

r   rr   c                 C   s   | j d}|rJ|d }|d }|dkr(|d t}tj|||d dd|dkr@|d t}tj|||d dd	|d
krJtj|dt )Ninfo
contractIdreasonzno-longer-effectivetimez%m-%d-%Y)r   datecontract_expiry_dateznot-effective-yet)r   r  contract_effective_dateznever-effective)r   )	ri   rl   strftimer   r
   AttachForbiddenExpiredAttachForbiddenNotYetAttachForbiddenNeverAttachExpiredToken)rr   r  r   r  r  r6   r6   r7   rg     s*   rg   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
    rZ   machineTokenInfocontractInfoidrD   )r   r   rP   Fr  N)rF   rG   r  r   r@   r   r   r   r^   cache_clearrl   r   r   )	rB   rH   orig_entitlements
orig_tokenr   r   contract_clientrespro   r6   r6   r7   refresh.  s*   






r$  c                 C   s   t | }| }|dg S )zDQuery available resources from the contract server for this machine.r   )r@   r{   rl   )rB   clientr   r6   r6   r7   get_available_resourcesO  s   r&  r]   c                 C   s   t | }||S )z/Query contract information for a specific token)r@   r~   )rB   r]   r%  r6   r6   r7   get_contract_informationV  s   
r'  override_selectorselector_valuesc                 C   s<   d}|   D ]\}}||f|  vr dS |t| 7 }q|S )Nr   )itemsOVERRIDE_SELECTOR_WEIGHTS)r(  r)  override_weightselectorvaluer6   r6   r7   _get_override_weight\  s   r/  r  series_namer   c           
      C   sz   i }||d}|r||d< |  di  |i }|r||td < t| dg }|D ]}t| d|}	|	r:|||	< q*|S )N)r   r   r    r   r   	overridesr-  )popr+  copydeepcopyrl   r/  )
r  r0  r   r    r1  r)  r   general_overridesoverrideweightr6   r6   r7   _select_overridesh  s"   


r8  r   c                 C   s   ddl m} tt| td| v gstd| |du r!t j	n|}| \}}| 
di }t||||}t| D ]%\}	}
|
 D ]\}}| d 
|}t|trY|| qC|| d |< qCq;d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)uaclient.clouds.identityr9  all
isinstancedictRuntimeErrorr`   r   r   r   rl   r8  sortedr*  r_   )r   r   r    r9  r0  r   _orig_entitlementr1  _weightoverrides_to_applykeyr.  currentr6   r6   r7   r	    s*   

r	  r  c              	   C   s   ddl m} g }| D ]H\}}|di dd}z	|| ||d}W n
 tjy-   Y qw |di di }|d}	|||	rT| \}
}|
rT|t	||d	 q|S )
Nr   r   r  r  r[   r  r  resourceToken)r"   r    )
r   r   r*  rl   r
   r  _should_enable_by_default
can_enabler   r!   )rB   r  r   enable_by_default_servicesent_name	ent_valuer    entr  rF  rH  r@  r6   r6   r7   get_enabled_by_default_services  s,   
rM  )T)FTr3   )NN)Qr3  loggingr   collectionsr   typingr   r   r   r   r   uaclient.files.machine_tokenfilesr   rF   uaclientr   r	   r
   r   r   r   r   r   r   -uaclient.api.u.pro.status.enabled_services.v1r   (uaclient.api.u.pro.status.is_attached.v1r   uaclient.configr   uaclient.defaultsr   uaclient.files.state_filesr   r   uaclient.httpr   uaclient.logr   re   r   r   rz   r   r   r   r}   r   r   r   r   r+  get_event_loggerr   	getLoggerreplace_top_level_logger_namer9   r   r!   
DataObjectr#   UAServiceClientr@   r=  rc   r?   boolr   r   HTTPResponseNamedMessagerg   r$  r&  r'  intr/  r8  r	  rM  r6   r6   r6   r7   <module>   s    ,W   C 


^



?
!






1
