
Ke6y                 @   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 d d d i Z, e j-   Z. e j/ e j0 e1   Z2 e j3 Gd d   d e j4   Z5 Gd d   d e  j6  Z7 e8 d d d     Z9 d! e e e: e f e e: e f e; e; d d" d# d$   Z< d% 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-   ZA e e e d. d/ d0    ZB e e: e e: e f d1 d2 d3    ZC e e; d. d4 d5    ZD e e: e: f e e: e: f eE d6 d7 d8    ZF d e e: e f e: e: e e: e eE e e: e f f d9 d: d;   ZG d d e e: e f e e: e e: d d< d= d>   ZH e e	 e5 eE f d. d? d@    ZI d S)A    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   variant   c               @   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            
   @   s  e  Z d  Z d Z e j e j d d d d g 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 d g e j d d d     Z d 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_urlZretry_sleepsr   r   Nc       	      C   s  | s t  j |  j  } |  j   } | j d d j |  i  |  j   } | j   | d <d | d | i } t |  } |  j	 t
 d | d | } | j d k r t j    n | j d	 k r t |  | j d
 k r t j d t
 d | j d | 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.
        Authorizationz	Bearer {}lastAttachment	machineIdactivityInfodataheadersi  i     urlcodebody)r   get_machine_idcfgr0   updateformat_get_activity_info	isoformat_support_old_machine_inforequest_urlAPI_V1_ADD_CONTRACT_MACHINEr3   r	   ZAttachInvalidTokenError_raise_attach_forbidden_messageContractAPIErrorr4   	json_dict)	selfcontract_tokenZattachment_dt
machine_idr0   activity_infor/   backcompat_dataresponser(   r(   r)   add_contract_machineJ   s(    
		z%UAContractClient.add_contract_machine)returnc             C   s   |  j    } |  j t d d | d d | d d | d d | d i } | j d k r| t j d t d | j d	 | j   | j S)
z=Requests list of entitlements available to this machine type.query_paramsarchitecturer   kernelvirtr1   r2   r3   r4   )r9   r<   API_V1_AVAILABLE_RESOURCESr3   r	   r?   r4   r@   )rA   rD   rF   r(   r(   r)   available_resourcesp   s    


		z$UAContractClient.available_resources)rB   rH   c             C   sw   |  j    } | j d d j |  i  |  j t d | } | j d k rp t j d t d | j d | j   | j	 S)Nr+   z	Bearer {}r0   r1   r2   r3   r4   )
r0   r7   r8   r<   API_V1_GET_CONTRACT_USING_TOKENr3   r	   r?   r4   r@   )rA   rB   r0   rF   r(   r(   r)   get_contract_using_token   s    		z)UAContractClient.get_contract_using_token)instancec            C   s   |  j  t j d | j  d | j } | j d k r | j j d d  } | rp t j	 |  t
 j d |   t
 j d t d | j d	 | 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_typer/   r1   message Z	error_msgr2   r3   r4   zcontract-token)r<   ,API_V1_GET_CONTRACT_TOKEN_FOR_CLOUD_INSTANCEr8   rR   Zidentity_docr3   r@   getLOGdebugr	   ZInvalidProImager?   r4   r6   write_cache)rA   rQ   rF   msgr(   r(   r)   %get_contract_token_for_cloud_instance   s    
			z6UAContractClient.get_contract_token_for_cloud_instanceT)machine_tokenresourcerC   	save_filerH   c             C   s   | s t  j |  j  } |  j   } | j d d j |  i  t j d | d |  } |  j | d | } | j d k r t	 j
 d t d | j d	 | j   | j j d
  r | j d
 | j d
 <| r |  j j d j |  | 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+   z	Bearer {}r]   machiner0   r1   r2   r3   r4   expireszmachine-access-{})r   r5   r6   r0   r7   r8   "API_V1_GET_RESOURCE_MACHINE_ACCESSr<   r3   r	   r?   r4   rV   r@   rY   )rA   r\   r]   rC   r^   r0   r2   rF   r(   r(   r)   get_resource_machine_access   s$    				z,UAContractClient.get_resource_machine_accessc             C   s	  |  j  j j } |  j  j j d  } t j |  j   } |  j   } t j	 d | d |  } |  j
   } | j d d j	 |  i  |  j | d | d | } | j d k r t j d	 | d
 | j d | j   | j r|  j  j } | j | d <|  j  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contractr_   r+   z	Bearer {}r0   r/   r1   r2   r3   r4   r.   N)r6   machine_token_filecontract_idr\   rV   r   r5   r9   API_V1_UPDATE_ACTIVITY_TOKENr8   r0   r7   r<   r3   r	   r?   r4   r@   write)rA   rf   r\   rC   Zrequest_datar2   r0   rF   r(   r(   r)   update_activity_token   s     			z&UAContractClient.update_activity_token)magic_tokenrH   c             C   s   |  j    } | j d d j |  i  y |  j t d | } WnA t j k
 r } z t j |  t j	    WYd d } ~ Xn X| j
 d k r t j    | j
 d k r t j    | j
 d k r t j d t d	 | j
 d
 | 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+   z	Bearer {}r0   Ni  i  r1   r2   r3   r4   )r0   r7   r8   r<   "API_V1_GET_MAGIC_ATTACH_TOKEN_INFOr	   UrlErrorrW   	exceptionConnectivityErrorr3   MagicAttachTokenErrorMagicAttachUnavailabler?   r4   r@   )rA   rj   r0   rF   er(   r(   r)   get_magic_attach_token_info   s$    		z,UAContractClient.get_magic_attach_token_infoc             C   s   |  j    } y |  j t d | d d } WnA t j k
 rn } z t j |  t j    WYd d } ~ Xn X| j d k r t j	    | j d k r t j
 d t d | j d	 | j   | j S)
z)Create a magic attach token for the user.r0   methodPOSTNi  r1   r2   r3   r4   )r0   r<   API_V1_NEW_MAGIC_ATTACHr	   rl   rW   rm   rn   r3   rp   r?   r4   r@   )rA   r0   rF   rq   r(   r(   r)   new_magic_attach_token  s"    		z'UAContractClient.new_magic_attach_token)rj   c             C   s  |  j    } | j d d j |  i  y |  j t d | d d } WnA t j k
 r } z t j |  t j	    WYd d } ~ Xn X| j
 d k r t j    | j
 d k r t j    | j
 d	 k r t j    | j
 d
 k rt j d t d | j
 d | j   d S)z)Revoke a magic attach token for the user.r+   z	Bearer {}r0   rs   ZDELETENi  i  i  r1   r2   r3   r4   )r0   r7   r8   r<   API_V1_REVOKE_MAGIC_ATTACHr	   rl   rW   rm   rn   r3   Z MagicAttachTokenAlreadyActivatedro   rp   r?   r4   )rA   rj   r0   rF   rq   r(   r(   r)   revoke_magic_attach_token3  s*    		z*UAContractClient.revoke_magic_attach_token)r\   rf   rC   rH   c             C   s  | s t  j |  j  } |  j   } | j d d j |  i  t j d | d |  } |  j   } |  j | d d d | d d	 | d	 d
 | d
 d | d d | d i } | j	 d k r t
 j d | d | j	 d | 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+   z	Bearer {}rd   r_   rs   ZGETr0   rI   rJ   r   rK   rL   r1   r2   r3   r4   r`   )r   r5   r6   r0   r7   r8   API_V1_GET_CONTRACT_MACHINEr9   r<   r3   r	   r?   r4   rV   r@   )rA   r\   rf   rC   r0   r2   rD   rF   r(   r(   r)   get_contract_machineN  s,    		


	z%UAContractClient.get_contract_machinec       	      C   s   | s t  j |  j  } |  j   } | j d d j |  i  d | d |  j   i } t |  } t j d | d |  } |  j	 | d | d d	 d
 | } | j
 d k r t j d | d | j
 d | 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+   z	Bearer {}r-   r.   rd   r_   r0   rs   rt   r/   r1   r2   r3   r4   r`   )r   r5   r6   r0   r7   r8   r9   r;   API_V1_UPDATE_CONTRACT_MACHINEr<   r3   r	   r?   r4   rV   r@   )	rA   r\   rf   rC   r0   r/   rE   r2   rF   r(   r(   r)   update_contract_machiney  s"    		z(UAContractClient.update_contract_machinec             C   s  d t  j   j d t  j   j d t  j   j d t  j   d t  j   d t  j   d t	 j
   i } t |  j  j rt |  j  j } t j   } d |  j j j p t  j |  j  d	 |  j j j d
 d d   | D d d d   | D d | r| j j   n d i } n i  } | |  S)z9Return a dict of activity info data for contract requestsdistributionrK   r   rJ   ZdesktoprL   ZclientVersionZ
activityIDZactivityToken	resourcesc             S   s   g  |  ] } | j   q Sr(   )name).0servicer(   r(   r)   
<listcomp>  s   	 z7UAContractClient._get_activity_info.<locals>.<listcomp>ZresourceVariantsc             S   s(   i  |  ] } | j  r | j | j  q Sr(   )Zvariant_enabledZvariant_namer   )r   r   r(   r(   r)   
<dictcomp>  s   	z7UAContractClient._get_activity_info.<locals>.<dictcomp>r,   N)r   get_release_infor}   Zget_kernel_infoZuname_releaser   Zget_dpkg_archZ
is_desktopZget_virt_typer   Zget_versionr   r6   is_attachedr   enabled_servicesr   readre   Zactivity_idr5   Zactivity_tokenZattached_atr:   )rA   Zmachine_infor   Zattachment_datarD   r(   r(   r)   r9     s,    z#UAContractClient._get_activity_info)r    r!   r"   Zcfg_url_base_attrr   ZretrysocketZtimeoutrG   r   strr   rN   rP   r   ZAutoAttachCloudInstancer[   r   boolrb   ri   rr   rv   rx   rz   r|   r9   r(   r(   r(   r)   r*   G   s&   $" "&"*#r*   )request_bodyc             C   s   |  j  d i   } d |  j  d  d | d | j  d  d d | j  d  d | j  d  d | j  d  d d	 d
 t j   j i i 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.
    r.   r-   rJ   osr}   rK   r   typeZLinuxrelease)rV   r   r   r   )r   rD   r(   r(   r)   r;     s    	r;   T)r6   past_entitlementsnew_entitlementsallow_enabler   rH   c             C   s  d d l  m } d } d } g  } xK| |   D]=}	 y | |	 }
 Wn t k
 rZ w/ Yn Xg  } y= t d |  d | j |	 i   d |
 d | d |  \ } } Wn t j k
 r } z8 t j |  d	 } | j	 |	  t j
 d
 |	 |
  WYd d } ~ Xq/ t k
 rR} z8 t j |  d	 } | j	 |	  t j d |	 |
  WYd d } ~ Xq/ X| r/ | r/ t j |	  q/ Wt j |  | rt 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_orderFr6   orig_access
new_accessr   r   Tz+Failed to process contract delta for %s: %rNz5Unexpected error processing contract delta for %s: %rfailed_servicesc             S   s   g  |  ] } | t  j f  q Sr(   )r   ZUNEXPECTED_ERROR)r   r   r(   r(   r)   r   *  s   	 z.process_entitlements_delta.<locals>.<listcomp>c             S   s   g  |  ] } | t  j f  q Sr(   )r   Z!E_ATTACH_FAILURE_DEFAULT_SERVICES)r   r   r(   r(   r)   r   0  s   	)uaclient.entitlementsr   KeyErrorprocess_entitlement_deltarV   r	   ZUbuntuProErrorrW   rm   appenderror	ExceptioneventZservice_processedZservices_failedZAttachFailureUnknownErrorZAttachFailureDefaultServices)r6   r   r   r   r   r   Zdelta_errorZunexpected_errorr   r   new_entitlementdeltasZservice_enabledrq   r(   r(   r)   process_entitlements_delta  sV    			r   F)r6   r   r   r   r   rH   c             C   sY  d d l  m } | r  t |  t j | |  } d } | rO| j d i   j d  } | sz | j d i   j d  } | s t j d | d |   | j d i   j d	 i   j d
 d  }	 y | d |  d | d |	  }
 Wn> t j k
 r!} z t	 j
 d |  |  WYd d } ~ Xn X|
 d |  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 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entitlementsZobligationsZuse_selectorrT   r6   r   r   z3Skipping entitlement deltas for "%s". No such classNZ
assume_yesr   )r   r   apply_contract_overridesr   get_dict_deltasrV   r	   Z InvalidContractDeltasServiceTypeZEntitlementNotFoundErrorrW   rX   Zprocess_contract_deltas)r6   r   r   r   r   r   r   Zretr   r   Zent_clsexcr   r(   r(   r)   r   6  s2    
	
r   )rF   rH   c             C   s   |  j  j d  } | r | d } | d } | d k ry | d j t  } t j d | d | d | d j d	    nk | d
 k r | d j t  } t j d | d | d | d j d	    n | d k r t j d |   t j    d  S)NinfoZ
contractIdreasonzno-longer-effectiveZtimerf   dateZcontract_expiry_datez%m-%d-%Yznot-effective-yetZcontract_effective_dateznever-effective)	r@   rV   Zstrftimer   r	   ZAttachForbiddenExpiredZAttachForbiddenNotYetZAttachForbiddenNeverZAttachExpiredToken)rF   r   rf   r   r   r(   r(   r)   r>   r  s&    

		r>   c             C   s   |  j  j } |  j } | d } | d d d } t |   } | j d | d |  } |  j  j |  t j j   | j	 d i   j	 d t j |    } |  j
 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 UbuntuProError: on failure to update contract or error processing
        contract deltas
    :raise UrlError: On failure during a connection
    rc   machineTokenInfocontractInfoidr\   rf   r-   z
machine-idr   FN)re   r   r\   r*   r|   rh   r   r5   cache_clearrV   rY   r   )r6   orig_entitlements
orig_tokenr\   rf   contract_clientresprC   r(   r(   r)   refresh  s"    		
	r   )r6   rH   c             C   s(   t  |   } | j   } | j d g   S)zDQuery available resources from the contract server for this machine.r~   )r*   rN   rV   )r6   clientr~   r(   r(   r)   get_available_resources  s    r   )r6   tokenrH   c             C   s   t  |   } | j |  S)z/Query contract information for a specific token)r*   rP   )r6   r   r   r(   r(   r)   get_contract_information  s    r   c             C   s2  |  j  } |  j j } | j d d  } | j d i   j d i   j d d   } | s[ d St |   } | j | |  } | j d i   j d i   j d d   } | r | n	 |  j j } |  j j | k r d S|  j j |  }	 xH t |	 j	    D]4 \ }
 } t
 j | j |
 i   |  } | r d Sq Wd S)	Nrc   rT   r   r   r   FZeffectiveToT)r\   re   r   rV   r*   rz   Zcontract_expiry_datetimeZget_entitlements_from_tokensorteditemsr   r   )r6   r   r   r\   rf   r   r   Zresp_expiryZ
new_expiryZcurr_entitlementsr   r   r   r(   r(   r)   is_contract_changed  s2    			r   )override_selectorselector_valuesrH   c             C   sQ   d } xD |  j    D]6 \ } } | | f | j    k r; d S| t | 7} q W| S)Nr   )r   OVERRIDE_SELECTOR_WEIGHTS)r   r   Zoverride_weightselectorvaluer(   r(   r)   _get_override_weight  s    r   )r   series_namerR   r   rH   c       
      C   s   i  } d | d | i } | r( | | d <|  j  d i   j  | i   } | rZ | | t d <t j |  j d g    } x6 | D]. } t | j  d  |  }	 |	 r| | | |	 <q| W| S)Nr   r   r   r   	overridesr   )popr   copydeepcopyrV   r   )
r   r   rR   r   r   r   r   Zgeneral_overridesoverrideZweightr(   r(   r)   _select_overrides  s    
r   )r   r   r   rH   c             C   s!  d d l  m } t t |  t  d |  k g  sF t d j |     | d k ra t j   j	 n | } |   \ } } |  j
 d i   } t | | | |  } x} t | j    D]i \ }	 }
 xZ |
 j   D]L \ } } |  d j
 |  } t | t  r| j |  q | |  d | <q Wq Wd 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RuntimeErrorr8   r   r   r   rV   r   r   r   r7   )r   r   r   r   r   rR   _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 St } t } |  j j } | d k r[ t j	 d  t j
 | f Sd | k or | k n r t j | f S| | k o d k  n r t j | f S| | k  r t j
 | f St 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   re   Zcontract_remaining_daysrW   Zwarningr'   r%   r&   r$   )r6   Zgrace_periodZpending_expiryZremaining_daysr(   r(   r)   get_contract_expiry_status>  s     r   )Jr   ZenumZloggingr   Z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=   ry   r{   rM   ra   rU   rg   rO   rk   ru   rw   r   Zget_event_loggerr   Z	getLoggerZreplace_top_level_logger_namer    rW   ZuniqueZEnumr   ZUAServiceClientr*   r   r;   r   r   r   r   ZHTTPResponseZNamedMessager>   r   r   r   r   intr   r   r   r   r(   r(   r(   r)   <module>   sj   (:
  &Q&7"%').