U
    M[^V                     @   sV  d Z ddlZddlm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 ddl
mZ dd	l
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dgZdd ZdUddZdd ZedddZ dd Z!dd  Z"d!d" Z#d#d$ Z$d%d& Z%d'd( Z&ee d)eej'd*d+ Z(d,d-eej)e*e*d.d/d0Z+ee d1eej'd2d3 Z,ee e-d4d5d6Z.ej)e*e-d7d8d9Z/ej)ee*e-d:d;d<Z0ej)ed=d>d?Z1ed@dA Z2eedBdC Z3dDdE Z4dFdG Z5dVdHdIZ6ee dJdK Z7dLdM Z8dWdNdOZ9dPdQ Z:e:dXdRdSZ;e<dTkrRe=e;  dS )Yz8Client to manage Ubuntu Advantage services on a machine.    Nwraps)config)contract)entitlements)
exceptions)status)util)version)identityZuaz{name} {command} [flags]zAUse {name} {command} --help for more information about a command.z}Account: {account}
Subscription: {subscription}
Valid until: {contract_expiry}
Technical support level: {tech_support_level}
z$https://auth.contracts.canonical.comzD%(asctime)s - %(filename)s:(%(lineno)d) [%(levelname)s]: %(message)sZtabularjsonc                    s   t   fdd}|S )zDecorator asserting root userc                     s   t  dkrt  | |S Nr   )osgetuidr   ZNonRootUserError)argskwargsf ./usr/lib/python3/dist-packages/uaclient/cli.pynew_f0   s    zassert_root.<locals>.new_fr   r   r   r   r   r   assert_root-   s    r   c                    s    fdd}|S )zDecorator asserting attached config.

    :param unattached_msg_tmpl: Optional msg template to format if raising an
        UnattachedError
    c                    s   t   fdd}|S )Nc                    sD   |j s:r.t| dd}j|d}t|}nt }| | |S )NnameNone)r   )is_attachedgetattrformatr   ZUnattachedError)r   cfgr   msg	exception)r   unattached_msg_tmplr   r   r   A   s    z/assert_attached.<locals>.wrapper.<locals>.new_fr   r   r!   r   r   wrapper@   s    z assert_attached.<locals>.wrapperr   )r!   r#   r   r"   r   assert_attached9   s    r$   c                    s   t   fdd}|S )z&Decorator asserting unattached config.c                    s   |j rt| | |S N)r   r   AlreadyAttachedErrorr   r   r   r   r   r   U   s    
z"assert_not_attached.<locals>.new_fr   r   r   r   r   assert_not_attachedR   s    r(   	operationc                    s    fdd}|S )z}Decorator ensuring that args.name is a valid service.

    :param operation: the operation name to use in error messages
    c                    s   t   fdd}|S )Nc                    s>   t | dr4| j}tj}|tjkr4t|j|d | |S )Nr   )r*   r   )	hasattrr   	ua_statusZ'MESSAGE_INVALID_SERVICE_OP_FAILURE_TMPLr   ENTITLEMENT_CLASS_BY_NAMEr   UserFacingErrorr   )r   r   r   Ztmpl)r   r*   r   r   r   e   s    

z>require_valid_entitlement_name.<locals>.wrapper.<locals>.new_fr   r   r)   r   r   r#   d   s    
z/require_valid_entitlement_name.<locals>.wrapperr   )r*   r#   r   r)   r   require_valid_entitlement_name^   s    r/   c                 C   s$   d| _ tjt| j d| _d| j_| S )z9Build or extend an arg parser for auto-attach subcommand.auto-attachr   commandFlags)prog
USAGE_TMPLr   NAMEusage
_optionalstitleparserr   r   r   auto_attach_parseru   s    r<   c                 C   sJ   t jtdd| _d| _d| j_| jdddtd | jd	d
ddd | S )z4Build or extend an arg parser for attach subcommand.zattach <token>r1   attachr3   token?z6token obtained for Ubuntu Advantage authentication: {})nargshelpz--no-auto-enableZstore_falseauto_enablez4do not enable any recommended services automatically)actiondestrA   )	r5   r   r6   r7   r4   r8   r9   add_argumentUA_AUTH_TOKEN_URLr:   r   r   r   attach_parser}   s"    rG   c                 C   s6   t jtdd}|| _d| _d| j_| jdddd | S )z4Build or extend an arg parser for detach subcommand.detachr1   r3   z--assume-yes
store_truez;do not prompt for confirmation before performing the detachrC   rA   )r5   r   r6   r7   r4   r8   r9   rE   r;   r7   r   r   r   detach_parser   s    rL   c                 C   sB   t jtddd }|| _d| _d| j_d| j_| jdddd	 | S )
z4Build or extend an arg parser for enable subcommand.enabler1    <name>	Argumentsr3   r   storez2the name of the Ubuntu Advantage service to enablerJ   	r5   r   r6   r7   r4   Z_positionalsr9   r8   rE   rK   r   r   r   enable_parser   s    rR   c                 C   sB   t jtddd }|| _d| _d| j_d| j_| jdddd	 | S )
z5Build or extend an arg parser for disable subcommand.disabler1   rN   rO   r3   r   rP   z3the name of the Ubuntu Advantage service to disablerJ   rQ   rK   r   r   r   disable_parser   s    rT   c              	   C   s\   t jtdd}|| _d| _tj| _t	d| _
| jddttd dtd d d	| j_| S )
z4Build or extend an arg parser for status subcommand.r   r1   u          Report current status of Ubuntu Advantage services on system.

        This shows whether this machine is attached to an Ubuntu Advantage
        support contract. When attached, the report includes the specific
        support contract details including contract name, expiry dates, and the
        status of each service on this system.

        The attached status output has four columns:

        * SERVICE: name of the service
        * ENTITLED: whether the contract to which this machine is attached
          entitles use of this service. Possible values are: yes or no
        * STATUS: whether the service is enabled on this machine. Possible
          values are: enabled, disabled, n/a (if your contract entitles
          you to the service, but it isn't available for this machine) or — (if
          you aren't entitled to this service)
        * DESCRIPTION: a brief description of the service

        The unattached status output instead has three columns. SERVICE
        and DESCRIPTION are the same as above, and there is the addition
        of:

        * AVAILABLE: whether this service would be available if this machine
          were attached. The possible values are yes or no.
        z--formatrP   r   z3output status in the specified format (default: {}))rC   choicesdefaultrA   r3   )r5   r   r6   r7   r4   argparseRawDescriptionHelpFormatterformatter_classtextwrapdedentdescriptionrE   STATUS_FORMATSr8   r9   rK   r   r   r   status_parser   s$    r^   rS   c                 C   s0   t j| j }||}| r dnd}|  |S )z_Perform the disable action on a named entitlement.

    @return: 0 on success, 1 otherwise
    r      )r   r-   r   rS   r   )r   r   ent_clsentitlementretr   r   r   action_disable   s
    rc   Fsilent_if_inapplicable)entitlement_namer   re   returnc                C   s*   t j|  }||}|j|d}|  |S )a  Perform the enable action on a named entitlement.

    (This helper excludes any messaging, so that different enablement code
    paths can message themselves.)

    :param entitlement_name: the name of the entitlement to enable
    :param cfg: the UAConfig to pass to the entitlement
    :param silent_if_inapplicable:
        don't output messages when determining if an entitlement can be
        enabled on this system

    @return: True on success, False otherwise
    rd   )r   r-   rM   r   )rf   r   re   r`   ra   rb   r   r   r   _perform_enable   s
    
rh   rM   c              	   C   sZ   t tj zt| W n, tjtjfk
rD   t	j
tjdd Y nX t| j|rVdS dS )z^Perform the enable action on a named entitlement.

    @return: 0 on success, 1 otherwise
    T)exc_infor   r_   )printr,   ZMESSAGE_REFRESH_ENABLEr   request_updated_contractr	   UrlErrorr   r.   loggingdebugMESSAGE_REFRESH_FAILURErh   r   r'   r   r   r   action_enable  s    
rp   )rg   c                 C   s   t || jdS )zXPerform the detach action for this machine.

    @return: 0 on success, 1 otherwise
    
assume_yes)_detachrr   r'   r   r   r   action_detach+  s    rt   )r   rr   rg   c           	      C   s   g }t jD ]"}|| }|jddr
|| q
|rnt|dkrBdnd}td| |D ]}td|j qX|s~t	 s~dS |D ]}|j
dd qt| }| jd }| jd	 d
 d }||| |   ttj dS )a=  Detach the machine from the active Ubuntu Advantage subscription,

    :param cfg: a ``config.UAConfig`` instance
    :param assume_yes: Assume a yes answer to any prompts requested.
         In this case, it means automatically disable any service during
         detach.

    @return: 0 on success, 1 otherwise
    T)Zsilentr_   s z,Detach will disable the following service{}:z    {}ZmachineTokenmachineTokenInfocontractInfoidr   )r   ZENTITLEMENT_CLASSESZcan_disableappendlenrj   r   r   r	   Zprompt_for_confirmationrS   r   UAContractClientmachine_tokenZdetach_machine_from_contractZdelete_cacher,   ZMESSAGE_DETACH_SUCCESS)	r   rr   Z
to_disabler`   Zentsuffixcontract_clientr}   Zcontract_idr   r   r   rs   5  s*    




rs   )r   r>   allow_enablerg   c                 C   s   zt j| ||d W n tjk
rn } z:t  t| W 5 Q R X ttj	 | 
  W Y dS d}~X Y n> tjk
r } zt|j | 
  W Y dS d}~X Y nX | jd d d }ttjj|d td| d d	S )
zDCommon functionality to take a token and attach via contract backend)r   r_   Nrw   rx   r   )contract_namer'   r   )r   rk   r	   rl   disable_log_to_consolerm   r    rj   r,   ZMESSAGE_ATTACH_FAILUREr   r   r.   Zwarningr   r}   ZMESSAGE_ATTACH_SUCCESS_TMPLr   action_status)r   r>   r   excr   r   r   r   _attach_with_tokenV  s4      


r   )r   rg   c              
   C   s   zt  }W n8 tjk
rD } z| jr0t| |W 5 d}~X Y nX t  }| jr| d}||krpt| td t	| dddkrtt
jt| }z|j|d}W nV tjk
r } z4|jrd|j  krd	k rn ntt
j|W 5 d}~X Y nX |r| d| |d
 S )aI  Detect cloud_type and request a contract token from identity info.

    :param cfg: a ``config.UAConfig`` instance

    :raise NonAutoAttachImageError: When not on an auto-attach image type.
    :raise UrlError: On unexpected connectivity issues to contract
        server or inability to access identity doc from metadata service.
    :raise ContractAPIError: On unexpected errors when talking to the contract
        server.
    :raise NonAutoAttachImageError: If this cloud type does not have
        auto-attach support.

    :return: contract token obtained from identity doc
    Nzinstance-idz:Re-attaching Ubuntu Advantage subscription on new instanceTrq   r   )instancei  i  ZcontractToken)r   Zcloud_instance_factoryr   r.   r   r&   Zget_instance_idZ
read_cacherj   rs   r,   Z!MESSAGE_DETACH_AUTOMATION_FAILUREr   r|   Z"request_auto_attach_contract_tokenZContractAPIErrorcodeZNonAutoAttachImageErrorZMESSAGE_UNSUPPORTED_AUTO_ATTACHZwrite_cache)r   r   eZcurrent_iidZprev_iidr   ZtokenResponser   r   r   '_get_contract_token_from_cloud_identityu  s<    




 r   c                 C   s   t |}t||ddS )NTr>   r   )r   r   )r   r   r>   r   r   r   action_auto_attach  s    r   c                 C   s$   | j sttjt|| j | jdS )Nr   )r>   r   r.   r,   ZMESSAGE_ATTACH_REQUIRES_TOKENr   rB   r'   r   r   r   action_attach  s      r   c               	   C   s4  d} t g}ttj }|D ]\}}|jr8d|j}nd}| j||j|d}t|dkrf|	| qg }|}t|dkr|
dd\}}	|d|	 qn||d	d| g qtjttjd
|tjtddtjtddd}
|
jdddd d|
j_|
jdddd}d|_|jddd}|jtd t| |jddd}t| |jtd |jddd}t| |jt d |jdd d}t!| |jt"d |jd!d"d}t#| |jt$d |jd#d$d}t%| |jt&d |jd%d&d}|jt'd |jd'd(td}|jt(d |jd)d*d}|jt)d |
S )+Nz - {name}: {description}{url}z ({})rv   )r   r\   urlP    r_   r   z   
z	[command]r1   )r4   rY   r\   r7   Zepilogz--debugrI   z&show all debug log messages to consolerJ   r3   zAvailable Commandsr2   )r9   rD   metavarTr   z/current status of all Ubuntu Advantage services)rA   )rC   r=   z7attach this machine to an Ubuntu Advantage subscriptionr0   z<automatically attach Ubuntu Advantage on supported platformsrH   z9remove this machine from an Ubuntu Advantage subscriptionrM   z:enable a specific Ubuntu Advantage service on this machinerS   z;disable a specific Ubuntu Advantage service on this machineZrefreshz7refresh Ubuntu Advantage services from contracts serverr
   zshow version of {}rA   zshow this help message and exit)*__doc__sortedr   r-   itemsZhelp_doc_urlr   r\   r{   rz   rsplitinsertextendjoinrW   ArgumentParserr6   rX   r5   EPILOG_TMPLrE   r8   r9   Zadd_subparsersZrequiredZ
add_parserZset_defaultsr   r^   rG   r   r<   r   rL   rt   rR   rp   rT   rc   action_refreshprint_versionaction_help)Zservice_line_tmplZdescription_linesZsorted_classesr   r`   r   Zservice_lineZwrapped_wordslineZwrapped_wordr;   Z
subparsersZparser_statusZparser_attachZparser_auto_attachZparser_detachZparser_enableZparser_disableZparser_refreshZparser_versionZparser_helpr   r   r   
get_parser  s           r   c                 C   s   |st  }| rT| jdkrT| }|d tjjjkrDt|d |d< t	t
| n>t| }tjjd ks~dtjj kr|dd}t	| dS )Nr   ZexpireszUTF-8u   —-r   )r   UAConfigr   r   r,   ZUserFacingStatusZINAPPLICABLEvaluestrrj   r   dumpsZformat_tabularsysstdoutencodingupperreplace)r   r   r   outputr   r   r   r     s    
r   c                 C   s   t t  d S r%   )rj   r
   Zget_versionZ_argsZ_cfgr   r   r   r   )  s    r   c                 C   sl   zt | W nN tjk
r\ } z.t  t| W 5 Q R X tt	j
W 5 d }~X Y nX tt	j dS r   )r   rk   r	   rl   r   rm   r    r   r.   r,   ro   rj   ZMESSAGE_REFRESH_SUCCESS)r   r   r   r   r   r   r   -  s    

r   c                 C   s   t    dS r   )r   Z
print_helpr   r   r   r   r   :  s    
r   c                 C   s  |dkrt jd }t }tt}t }|| d}|j	D ]L}t
|dr@t
|jdr@|jjdkr@||  || |d d} qq@|sttj}|| ||  |d || t d	krt|}	|	  |	d
 t|}
|
| |
| ||
 dS )z3Setup console logging and debug logging to log_fileNlog_fileFstreamr   z<stderr>consoleTr   i  )r   ZCONFIG_DEFAULTSr	   ZLogFormatterrm   Z	FormatterDEFAULT_LOG_FORMATZ	getLoggerZsetLevelZhandlersr+   r   r   ZsetFormatterZset_nameZStreamHandlerr   stderrZ
addHandlerr   r   pathlibPathZtouchchmodZFileHandler)console_level	log_levelr   Zconsole_formatterZlog_formatterrootZstderr_foundZhandlerr   Zlog_file_pathZfilehandlerr   r   r   setup_logging?  s:    















r   c                    s    fdd}|S )Nc                     s  z | |W S  t k
rX   t  td W 5 Q R X tdtjd td Y n6 tj	k
r } zdt 8 |j
|d}|j
rtj}ntj}t|jf | W 5 Q R X ttjtjd td W 5 d }~X Y n tjk
r@ } zFt  t|j W 5 Q R X td|jtjd t|j W 5 d }~X Y nN tk
r   t  td W 5 Q R X ttjtjd td Y nX d S )NKeyboardInterruptzInterrupt received; exiting.)filer_   )r   errorz{}z&Unhandled exception, please file a bug)r   r	   r   rm   r    rj   r   r   exitrl   r   r,   Z$LOG_CONNECTIVITY_ERROR_WITH_URL_TMPLZLOG_CONNECTIVITY_ERROR_TMPLr   ZMESSAGE_CONNECTIVITY_ERRORr   r.   r   Z	exit_code	ExceptionZMESSAGE_UNEXPECTED_ERROR)r   r   r   Zmsg_argsZmsg_tmplfuncr   r   r#   d  s4    



z#main_error_handler.<locals>.wrapperr   )r   r#   r   r   r   main_error_handlerc  s    r   c                 C   s   | s
t j} t }| dd  }|s:|  td t d |j|d}t }|j	}|j
r`tjntj}t|||j t
d|  |||S )Nr_   z%Try 'ua --help' for more information.)r   zExecuted with sys.argv: %r)r   argvr   Zprint_usagerj   r   
parse_argsr   r   r   rn   rm   DEBUGINFOr   r   rC   )Zsys_argvr;   Zcli_argumentsr   r   r   r   r   r   r   main  s    
r   __main__)N)NN)N)N)>r   rW   	functoolsr   r   rm   r   r   r   rZ   Zuaclientr   r   r   r   r   r,   r	   r
   Zuaclient.cloudsr   r6   r5   r   ZSTATUS_HEADER_TMPLrF   r   r]   r   r$   r(   r   r/   r<   rG   rL   rR   rT   r^   Z&MESSAGE_ENABLE_FAILURE_UNATTACHED_TMPLrc   r   boolrh   rp   intrt   rs   r   r   r   r   r   r   r   r   r   r   r   r   __name__r   r   r   r   r   <module>   s   
3"  2

Z

$!
