a >hA@sZddlZddlZddlZddlmZddlZddlmZmZddl m Z GdddZ dS)N)quote)ConnectionExceptionCommandTimeoutException)boldc@s eZdZdZdZdZddZddZdd Zd d Z d d Z e ddZ e ddZ e ddZeddZeddZddZddZddZddZd d!Ze d"d#Zd=d'd(Zd)d*Zd+d,Zd-d.Zd/d0Zd1d2Zd3d4Zd5d6Zd7d8Z d9d:Z!d;d<Z"dS)>RemoteTransportaOThe base class used for defining supported remote transports to connect to remote nodes in conjunction with `sos collect`. This abstraction is used to manage the backend connections to nodes so that SoSNode() objects can be leveraged generically to connect to nodes, inspect those nodes, and run commands on them. Z undefinedNcCsF||_|d|_|d|_|d|_d|_td|_td|_dS)NZ cmdlineoptstmpdir need_sudoZsosZsos_ui) addressoptsrr _hostnameloggingZ getLoggersoslogZui_log)selfr ZcommonsrE/usr/lib/python3.9/site-packages/sos/collector/transports/__init__.py__init__#s    zRemoteTransport.__init__cCsd}t|d|S)zUAttempts to obfuscate sensitive information in log messages such as passwordsz>(?P(pass|key|secret|PASS|KEY|SECRET).*?=)(?P.*?\s)z\g****** )resub)rmsgZregrrr_sanitize_log_msg,sz!RemoteTransport._sanitize_log_msgcCs8tdd}d|jd|d|}|j|dS)z#Used to print and log info messages[:] N)inspectstackhostnamer inforrcallerZlmsgrrrlog_info2szRemoteTransport.log_infocCs8tdd}d|jd|d|}|j|dS)z$Used to print and log error messagesrrrrrN)rrrr errorrrrr log_error8szRemoteTransport.log_errorcCsB||}tdd}d|jd|d|}|j|dS)z$Used to print and log debug messagesrrrrrN)rrrrr debug)rrr rrr log_debug>s zRemoteTransport.log_debugcCs|jrd|jvr|jS|jS)NZ localhost)r r rrrrrEszRemoteTransport.hostnamecCsdS)zIs the transport __currently__ connected to the node, or otherwise capable of seamlessly running a command or similar on the node? Frr&rrr connectedKszRemoteTransport.connectedcCsdS)aThis is the command string needed to leverage the remote transport when executing commands. For example, for an SSH transport this would be the `ssh ` string prepended to any command so that the command is executed by the ssh binary. This is also referenced by the `remote_exec` parameter for policies when loading a policy for a remote node Nrr&rrr remote_execRs zRemoteTransport.remote_execcCs^|tur||S||jddd|jrP|jtjurP||jn |ddS)N_ z Transport Detailed Helpz5Detailed information not available for this transport)rdisplay_self_help set_titlenametitlereplace__doc__add_text)clssectionrrr display_help^s zRemoteTransport.display_helpcCs|d|dtdd|dtdd|dd d lm}|D]B}td |}d |d }|jdd|d|dddqPdS)NzSoS Remote Transport Helpzb Transports define how SoS connects to nodes and executes commands on them for the purposes of an z sos collectz run. Generally, this means transports define how commands are wrapped locally so that they are executed on the remote node(s) instead.zTransports are generally selected by the cluster profile loaded for a given execution, however users may explicitly set one using 'z--transport=$transport_namezI'. Note that not all transports will function for all cluster/node types.zBy default, OpenSSH Control Persist is attempted. Additional information for each supported transport is available in the following help sections: r) TRANSPORTSzcollect.transports.zThe 'z ' transportr*z>8z<45z<30F)newline)r,r1rZsos.collector.sosnoder5lower)r2r3r5Z transportZ_secZ_descrrrr+ls,  z!RemoteTransport.display_self_helpcCs ||r|js|dSdS)zPerform the connection steps in order to ensure that we are able to connect to the node for all future operations. Note that this should not provide an interactive shell at this time. TF)_connectr _get_hostnamerpasswordrrrconnects  zRemoteTransport.connectcCstd|jddS)zActually perform the connection requirements. Should be overridden by specific transports that subclass RemoteTransport Transport z does not define connectNNotImplementedErrorr-r:rrrr8s zRemoteTransport._connectc Csd}d}|dkr|d|dz||r4WdSWn>tyt}z&|d|d||}WYd }~n d }~00|d7}q|d td |d S) zAttempts to reconnect to the node using the standard connect() but does not do so indefinitely. This imposes a strict number of retry attempts before failing out runknownzAttempting reconnect (#z ) to nodeTz Attempt #z exception: Nz7Unable to reconnect to node after 5 attempts, aborting.zlast exception from transport: )r%r< Exceptionr#r)rr;attemptsZlast_errerrrrr reconnects    zRemoteTransport.reconnectc Cs\z"|r|dn |dWn4tyV}z|d|WYd}~n d}~00dS)zfPerform whatever steps are necessary, if any, to terminate any connection to the node z#Successfully disconnected from nodez;Unable to successfully disconnect, see log for more detailszFailed to disconnect: N) _disconnectr%r#rB)rrDrrr disconnects  zRemoteTransport.disconnectcCstd|jddS)Nr=z does not define disconnectr>r&rrrrFs zRemoteTransport._disconnectcCsdS)z Transports may override this to control when/if commands executed over the transport needs to utilize a shell on the remote host. Frr&rrr _need_shellszRemoteTransport._need_shellFautocCsZ|d||dus,|dkr(|jrJndrJdt|}|d||||||S)aRun a command on the node, returning its output and exit code. This should return the exit code of the command being executed, not the exit code of whatever mechanism the transport uses to execute that command :param cmd: The command to run :type cmd: ``str`` :param timeout: The maximum time in seconds to allow the cmd to run :type timeout: ``int``` :param need_root: Does ``cmd`` require root privileges? :type need_root: ``bool`` :param env: Specify env vars to be passed to the ``cmd`` :type env: ``dict`` :param use_shell: Does ``cmd`` require execution within a shell? :type use_shell: ``bool`` or ``auto`` for transport-determined :returns: Output of ``cmd`` and the exit code :rtype: ``dict`` with keys ``output`` and ``status`` zRunning command TrJFz /bin/bash -c z Shell requested, command is now )r%rHr_run_command_with_pexpect)rcmdtimeout need_rootenvZ use_shellrrr run_commandszRemoteTransport.run_commandcCs |jdt|}|}|S)aFormat the command in the way needed for the remote transport to successfully execute it as one would when manually executing it :param cmd: The command being executed, as formatted by SoSNode :type cmd: ``str`` :returns: The command further formatted as needed by this transport :rtype: ``str`` r*)r(rlstrip)rrLrrr_format_cmd_for_execs z$RemoteTransport._format_cmd_for_execc Cs||}|sd}ztj|d|d}Wn>tjjyd}z"||jdddWYd}~Sd}~00tjtjg}|r|j j dkr| dd g|j ||d }|d vr| |||j ||d }|d kr|j} ||j| dS|d krt||d|d|dddS)aExecute the command using pexpect, which allows us to more easily handle prompts and timeouts compared to directly leveraging the subprocess.Popen() method. :param cmd: The command to execute. This will be automatically formatted to use the transport. :type cmd: ``str`` :param timeout: The maximum time in seconds to run ``cmd`` :type timeout: ``int`` :param need_root: Does ``cmd`` need to run as root or with sudo? :type need_root: ``bool`` :param env: Any env vars that ``cmd`` should be run with :type env: ``dict`` Nzutf-8)encodingrO)statusoutputrootz\[sudo\] password for .*:z Password:rM)rrrzUnexpected index z from pexpect: i)rRpexpectZspawn exceptionsZExceptionPexpectr%valueZEOFZTIMEOUTr Zssh_userextendexpect_send_pexpect_passwordbeforecloseZ exitstatusr) rrLrMrNrOresultrDZ_expectsindexoutrrrrKs4      z)RemoteTransport._run_command_with_pexpectcCsv|dkr>|jjs.|jjs.d}||t|||jjn4|dkrr|jjsdd}||t|||jjdS)a`Handle password prompts for sudo and su usage for non-root SSH users :param index: The index pexpect.spawn returned to match against either a sudo or su prompt :type index: ``int`` :param result: The spawn running the command :type result: ``pexpect.spawn`` rZz>Unable to run command: sudo password required but not providedrz5Unable to run command as root: no root password givenN)r Zsudo_pwZ nopasswd_sudor#rBZsendlineZ root_password)rrdrcrrrrr`;s   z&RemoteTransport._send_pexpect_passwordcCsJ|d}|ddkr$|d|_|js2|j|_|d|j|jS)zDetermine the hostname of the node and set that for future reference and logging :returns: The hostname of the system, per the `hostname` command :rtype: ``str`` rrVrrWzHostname set to )rPstripr r r!)rZ_outrrrr9Ss  zRemoteTransport._get_hostnamec Csd}zJ|dkr@|d7}|||}|r,WdS|d|dq|dWdSty}z,|d |d |d ||WYd }~n d }~00d S) aZCopy a local file, fname, to dest on the remote node :param fname: The name of the file to copy :type fname: ``str`` :param dest: Where to save the file to remotely :type dest: ``str`` :returns: True if file was successfully copied to remote, or False :rtype: ``bool`` rrrTzFile copy attempt  failedz!File copy failed after 3 attemptsFz1Exception encountered during config copy attempt  for : N)_copy_file_to_remoter!rBr#rfnamedestrCretrDrrrcopy_file_to_remotecs&   z#RemoteTransport.copy_file_to_remotecCstd|jddSNr=z does not support file copyingr>rrlrmrrrrj~s z$RemoteTransport._copy_file_to_remotec Csd}zJ|dkr@|d7}|||}|r,WdS|d|dq|dWdSty}z,|d |d |d ||WYd }~n d }~00d S) a_Copy a remote file, fname, to dest on the local node :param fname: The name of the file to retrieve :type fname: ``str`` :param dest: Where to save the file to locally :type dest: ``str`` :returns: True if file was successfully copied from remote, or False :rtype: ``bool`` rrArTzFile retrieval attempt rgz&File retrieval failed after 5 attemptsFz/Exception encountered during retrieval attempt rhriN)_retrieve_filer!rBr#rkrrr retrieve_files&   zRemoteTransport.retrieve_filecCstd|jddSrpr>rqrrrrrs zRemoteTransport._retrieve_filecCs|d|||S)zRead the given file fname and return its contents :param fname: The name of the file to read :type fname: ``str`` :returns: The content of the file :rtype: ``str`` z Reading file )r% _read_file)rrlrrr read_files zRemoteTransport.read_filecCst|jd|dd}|ddkr(|dSd|dvrH|d|d n(|d |d |dd d ddS)Nzcat rYrVrrWz No such filezFile z does not exist on nodezError reading rirrrU)rPr%r#split)rrlresrrrrts   zRemoteTransport._read_file)rIFNrJ)#__name__ __module__ __qualname__r0r-Z default_userrrr!r#r%propertyrr'r( classmethodr4r+r<r8rErGrFrHrPrRrKr`r9rorjrsrrrurtrrrrrsL          &9 r) rr rZshlexrr[Zsos.collector.exceptionsrrZ sos.utilitiesrrrrrr s