InteractiveSession¶
This class is intended to be a wrapper around pexpect. The reason for implementing such a wrapper is to avoid having several testcases repeating the same sequences of calls to be pexpect.sendline() / pexpect.spawn() / pexpect.read() e.t.c. Furthermore the intention is to protect users from sequences of calls to these methods that could lead to erroneous results (for example executing two successive commands and not being able to properly distinguish/split the results printed to the terminal from each one).
Major functionality provided by the class is the ability to execute commands in a pseudo-terminals and getting back any bytes sent to it from the command.
This requires sending the command to the terminal, consuming any ‘waste’ printed on it and waiting for the shell prompt in order to be able to execute the next command. Since its type of shell (like bash) provides its own prompt, in order to keep track of prompt changes (for example invoking python shell from bash shell) a stack of objects is used (top object represents the currently active shell).
Each such object in the stack describes allowed actions and way of working of the currently active shell.
Recommended reading: basic knowledge of pexpect would help in understanding and using this library properly.
Bash Shell¶
This example describes how to start a bash shell and execute some commands:
s = InteractiveSession()
s.spawn(BashShell())
print s.current_shell().exec_command("ls -l")
print s.current_shell().exec_command("echo Hello")
print s.current_shell().exec_command("ls -l /some/non/existing/dir")
if s.current_shell().get_status_code() != 0:
print "Command failed!!"
SSH Connection¶
This example describes how to start an SSH connection and execute command on remote machine:
s = InteractiveSession()
s.spwan(SshShell("1.2.3.4", "username", "password"))
print s.current_shell().exec_command("ls -l")
print s.current_shell().get_status_code()
Stacking of Shells¶
This example describes how stacking of different shells work:
s = InteractiveSession()
s.spawn(BashShell())
print s.current_shell().exec_command("ls -l")
s.push(SshShell("1.2.3.4", "username", "password"))
s.push(PythonShell())
print s.current_shell().exec_command(
"print 'I am python on the remote host...'")
s.pop()
print s._current_shell().exec_command(
"echo 'I am bash on the remote host...'")
s.pop()
print s._current_shell().exec_command("echo 'I am back...'")
User Input¶
Executing commands that require user input:
s = InteractiveSession()
s.spawn(BashShell())
s.current_shell().exec_command("rm my_important_file",
confirmation_msg="Are you sure (Y/n):",
confirmation_rsp="y')
s.current_shell().exec_command("rm my_other_file1 my_other_file2",
confirmation_msg=[
"Are you sure (Y/n):",
"Are you sure (Y/n):"], ["y", "y"])
-
class
crl.interactivesessions.InteractiveSession.
InteractiveSession
(dump_received=None, dump_outgoing=None)¶ Bases:
object
InteractiveSession is a wrapper class of pexpect.
Two arguments for storing the sent and the received data to files are provided. By default, nothing is stored. If dump_received is given, then all the bytes received from the pseudo-terminal are stored to the file with full path dump_received. Similarly dump_outgoing is a path to a file to where all the sent data is stored.
-
__init__
(dump_received=None, dump_outgoing=None)¶ x.__init__(…) initializes x; see help(type(x)) for signature
-
current_shell
()¶ Return the currently active shell or raise exception if there is no shell
-
get_parent_shell
()¶ Returns the shell below currently active shell
-
pop
()¶ Should be called in order to terminate currently active shell and return to the previous one.
Note
The originally spawned shell is not part of the shell stack.
Example:
s = InteractiveSession() s.spawn(BashShell()) s.push(SshShell("1.2.3.4", "username", "password")) s.current_shell().exec_command("remote host command here") s.pop() s.current_shell().exec_command("local host command here")
-
pop_until
(shell)¶ Pop shells from the stack, until the received prompt matches the one of ‘shell’.
Should be used to ensure the shell stack is in a known state (e.g. at the beginning of each test case reusing the same
InteractiveSession
.)
-
push
(shell, timeout=30)¶ Should be called for any action that would change the command prompt. Failing to keep this rule will result in unpredictable results from Shell.exec_command set the last shell’s prompt back in case of ShellStartError (hostname is wrong in the started shell)
- Args:
- shell: any class implementing the Shell interface
Example:
s = InteractiveSession() s.spawn(BashShell() s.push(SshShell("1.2.3.4", "username", "password"))
-
spawn
(shell)¶ Thin wrapper around pexpect.spawn. This method is the first one that should be called in order to create a new terminal.
- Args:
- shell: any class implementing the Shell interface
Example:
s = InteractiveSession() s.spawn(BashShell())
-
-
exception
crl.interactivesessions.InteractiveSession.
UnexpectedPrompt
(received, expected)¶ Bases:
crl.interactivesessions.interactivesessionexceptions.InteractiveSessionError
Prompt received from terminal does not match with expected prompt.
-
__init__
(received, expected)¶ x.__init__(…) initializes x; see help(type(x)) for signature
-
-
exception
crl.interactivesessions.InteractiveSession.
UnknownShellState
¶ Bases:
crl.interactivesessions.interactivesessionexceptions.InteractiveSessionError
Raised when pop_until() results in an unexpected shell stack state.
InteractiveSession Shells¶
Shells serve as front-ends for the pseudo terminal by providing initialization and finalization methods of a new shell in the pseudo terminal as well as the pseudo terminal shell specific functionality.
In this section available shells as well as the base classes for them are documented.
Shell Abstract Bases¶
AutoCompletableShell¶
-
class
crl.interactivesessions.shells.autocompletableshell.
AutoCompletableShell
(tty_echo=False)¶ Bases:
crl.interactivesessions.shells.shell.Shell
Several shells support tab autocompletion. Any custom shell implementation can inherit from this class in order to provide such functionality.
- Args:
cmd: part of a command.
autocompletion_trigger: characters that the user would press to trigger autocompletion (for example
'\t'
for bash)timeout: how much time to wait (in seconds) for the shell prompt to reappear after the tab completion
Autocompletion works in different ways for different shells. Furthermore in some cases the autocompleted content just appears in the same line that the user was typing the command while in other cases it is printed below this line followed by a new prompt. The default implementation that can be reused by Shell derivatives works by expecting the new prompt for a finite amount of time. If the prompt does not appear then the implementation assumes that autocompletion occured on the same line and returns as a result any bytes printed to the terminal until the timeout occured.
Shell¶
-
class
crl.interactivesessions.shells.shell.
Shell
(tty_echo=False)¶ This is the basic interface that any new shell should implement.
-
exec_command
(cmd, timeout=-1)¶ Execute a command and return and text printed to the terminal.
- Args:
cmd: string containing the command to be executed
timeout: how many seconds to wait for command completion
- Returns:
- The terminal output of the command execution.
-
exec_command_expecting_more
(cmd, more_prompt='--More--')¶ This method is a variation of
exec_command()
that can be used when the command generates paged output. The method returns a tuple (more_counter, output), where more_counter is an integer representing the number of occurences of the more_prompt and output contains any text directed to the terminal (including more_prompt).
-
exec_prompting_command
(cmd, responses, timeout=-1)¶ Execute a command that prompts for additional input.
responses should be a list of tuples, each tuple specifying the prompt(s) expected, and the response to be sent.
- Args:
cmd(str): Command to be executed.
responses(list of tuples): Expected prompts.
timeout(number): Timeout for receiving each prompt.
- Returns:
- Terminal output.
-
exit
()¶ Close this shell.
-
get_prompt
()¶ Get the expected prompt for this shell.
-
get_prompt_from_terminal
(empty_command='', timeout=-1)¶ Send terminal an empty command which should cause terminal to send back the prompt. First, expected prompt is waited for timeout. If succesful, the expected prompt is returned but if timeout occurs, then output read so far is returned.
-
get_start_cmd
()¶ Return the command to be run in order to start this shell.
This method will be called by InteractiveSession.spawn() and InteractiveSession.push().
-
get_status_code
(timeout=5)¶ Abstract method. Implementation must return the status of the last command executed.
Returned value should be an integer indicating success/failure of the last command executed.
The default timeout DEFAULT_STATUS_TIMEOUT is globally adjustable:
>>> from crl.interactivesessions.shells.shell import ( ... DefaultStatusTimeout, ... DEFAULT_STATUS_TIMEOUT, ... Shell) >>> >>> class ExampleShell(Shell): ... def get_start_cmd(self): ... pass ... def start(self): ... pass ... def exit(self): ... pass ... def get_status_code(self, timeout=DEFAULT_STATUS_TIMEOUT): ... return float(timeout) >>> >>> DefaultStatusTimeout.set(20) >>> ExampleShell().get_status_code() 20.0
-
is_terminal_prompt_matching
(terminal_prompt)¶ Return True if and only if prompt is matching with the terminal output.
Args:
terminal_prompt(str): expected terminal prompt
-
start
()¶ Initialize the new shell.
This method will be called after running the command provided by get_start_cmd() and provides ability for the new shell to initialize itself.
-
-
exception
crl.interactivesessions.shells.shell.
TimeoutError
(output)¶ Raised when remote command execution times out.
Built-In Shells¶
BashShell¶
-
exception
crl.interactivesessions.shells.bashshell.
BashError
¶ Bases:
crl.interactivesessions.interactivesessionexceptions.InteractiveSessionError
Raised by BashShell errors
-
class
crl.interactivesessions.shells.bashshell.
BashShell
(cmd='bash', confirmation_msg=None, confirmation_rsp='Y', tty_echo=False, set_rand_promt=True, init_env=None, workdir=None, **kwargs)¶ Bases:
crl.interactivesessions.shells.autocompletableshell.AutoCompletableShell
InteractiveSession Shell interface for bash.
- Args:
cmd (str): command to start bash shell, default: bash
confirmation_msg (str): string to expect for confirmation to start bash shell
confirmation_rsp (str): expected response to confirmation
tty_echo (bool): terminal echo value to be set when started in spawn
- init_env (str): path to the file to be sourced in init or
- bash command to be executed if starts with ‘content:’. example: ‘content: alias python=python3’ default: None
workdir (bool): change to this directory in start
banner_timeout (float): timeout in seconds between lines in the start banner, default 0.1
-
exit
()¶ Close this shell.
-
get_pid
()¶ Returns the pid of the last crated process using ‘echo $!’
-
get_start_cmd
()¶ Return the command to be run in order to start this shell.
This method will be called by InteractiveSession.spawn() and InteractiveSession.push().
-
get_status_code
(timeout=5)¶ Returns the status code of the last executed command using ‘echo $?’
The default status timeout is globally adjustable. The setting
>>> from crl.interactivesessions.shells.shell import DefaultStatusTimeout >>> DefaultStatusTimeout.set(20)
sets default status timeout to 20 seconds.
-
scp_copy_file
(source_file, dest_ip, dest_user, dest_pass, dest_file)¶ Copy a file from localhost to a destination. This method is just a convenience wrapper around scp command line tool. Same results could be achieved using exec_command.
- Args:
source_file: name of the local file to be transfered
dest_ip: ip of the host that should receive the file
dest_user: username for host login
dest_pass: password for host login
dest_file: where to put the file on the destination host
-
scp_download_file
(source_file, source_ip, source_user, source_pass, dest_file)¶ Download a file from a remote machine to local. This method is just a convenience wrapper around scp command line tool. Same results could be achieved using exec_command.
- Args:
source_file: name of the remote file to be transfered
source_ip: ip of the host that contains the file
source_user: username for host login
source_pass: password for host login
dest_file: where to put the file on the local machine
-
start
()¶ Initialize the new shell.
This method will be called after running the command provided by get_start_cmd() and provides ability for the new shell to initialize itself.
-
terminate_process_forced
(pid)¶ This method send signal -9 to a process forcing its termination.
Note
Consider using exec_command(“kill -9 my_pid”)
After executing kill -9 … in a bash shell, the shell itself prints output notifying the user that some process was killed. This output is not guaranteed to be printed before the prompt that appears once the user hits Enter and can confuse exec_command.
-
exception
crl.interactivesessions.shells.bashshell.
BashShellTypeError
¶ Bases:
exceptions.TypeError
-
exception
crl.interactivesessions.shells.bashshell.
CdToWorkdirFailed
¶ Bases:
crl.interactivesessions.interactivesessionexceptions.InteractiveSessionError
-
exception
crl.interactivesessions.shells.bashshell.
FailedToStartApplication
¶ Bases:
crl.interactivesessions.interactivesessionexceptions.InteractiveSessionError
-
exception
crl.interactivesessions.shells.bashshell.
FailedToTerminateProcess
¶ Bases:
crl.interactivesessions.interactivesessionexceptions.InteractiveSessionError
-
exception
crl.interactivesessions.shells.bashshell.
ScpError
¶ Bases:
crl.interactivesessions.interactivesessionexceptions.InteractiveSessionError
-
exception
crl.interactivesessions.shells.bashshell.
SourceInitFileFailed
¶ Bases:
crl.interactivesessions.interactivesessionexceptions.InteractiveSessionError
NamespaceShell¶
-
class
crl.interactivesessions.shells.namespaceshell.
NamespaceShell
(namespace, *args, **kwargs)¶ Bases:
crl.interactivesessions.shells.bashshell.BashShell
This class can be used in order to start a bash shell inside a given namespace
Example:
s = InteractiveSession() s.spawn(SshShell("open_stack_controller", "username", "password")) s.push(NamespaceShell("qdhcp-1234...") s.push(SshShell("virtual_machine", "username", "password")) s.current_shell().exec_command("ls -l")
-
get_start_cmd
()¶ Return the command to be run in order to start this shell.
This method will be called by InteractiveSession.spawn() and InteractiveSession.push().
-
start
()¶ Initialize the new shell.
This method will be called after running the command provided by get_start_cmd() and provides ability for the new shell to initialize itself.
-
PythonShell¶
-
exception
crl.interactivesessions.shells.pythonshell.
PythonRunNotStarted
¶ Bases:
crl.interactivesessions.interactivesessionexceptions.InteractiveSessionError
-
class
crl.interactivesessions.shells.pythonshell.
PythonShell
(start_cmd='python', tty_echo=False, readline_init=None)¶ Bases:
crl.interactivesessions.shells.pythonshellbase.PythonShellBase
The path to Python executable can be altered via start_cmd.
Args:
start_cmd: command to start Python shell (Default: python)
tty_echo: if True then commands are echoed in shell. (Default: False)
- readline_init: If not None, then readline initialization commands in
- readline_init string are executed in terminal in start. Please see syntax from readline man pages (Default: None).
-
start
()¶ Initialize the new shell.
This method will be called after running the command provided by get_start_cmd() and provides ability for the new shell to initialize itself.
-
transfer_text_file
(source_path, destination_dir='')¶ Transfer the text file source_path to current host of the shell
SshShell¶
-
exception
crl.interactivesessions.shells.sshshell.
SshError
¶ Bases:
crl.interactivesessions.interactivesessionexceptions.InteractiveSessionError
Raised when
SshShell
start fails
-
class
crl.interactivesessions.shells.sshshell.
SshShell
(ip, username=None, password=None, tty_echo=False, second_password=None, port=None, init_env=None)¶ Bases:
crl.interactivesessions.shells.bashshell.BashShell
This class can be used in order to start a remote bash shell. See also
BashShell
.Args:
ip: IP address of the host
username: login username
password: login passowrd. If not given, passwordless login is expected.
tty_echo: If True, then terminal echo is set on.
- second_password: if not None, this password is send to terminal after
- the first password succesfully aplied.
- port: If port is not None, alternate port is used in the connection
- instead of the detfault 22.
- init_env: Path to initialization file which is sourced after the all
- other initialization is done.
For setting timeout for reading login banner, i.e. message-of-day, please use
msgreader.MsgReader.set_timeout()
.-
check_start_success
()¶ This method is called right after the shell is pushed and the prompt is not set yet. To be implemented in derivative classes if needed Should raise ShellStartError if not successful.
-
exit
()¶ Close this shell.
-
get_start_cmd
()¶ Return the command to be run in order to start this shell.
This method will be called by InteractiveSession.spawn() and InteractiveSession.push().
SftpShell¶
-
exception
crl.interactivesessions.shells.sftpshell.
SftpConnectionError
¶ Bases:
crl.interactivesessions.interactivesessionexceptions.InteractiveSessionError
-
class
crl.interactivesessions.shells.sftpshell.
SftpShell
(ip, username, password, cmd='sftp')¶ Bases:
crl.interactivesessions.shells.shell.Shell
SFTP Shell
-
exit
()¶ Close this shell.
-
get_prompt
()¶ Get the expected prompt for this shell.
-
get_start_cmd
()¶ Return the command to be run in order to start this shell.
This method will be called by InteractiveSession.spawn() and InteractiveSession.push().
-
get_status_code
(timeout=5)¶ Abstract method. Implementation must return the status of the last command executed.
Returned value should be an integer indicating success/failure of the last command executed.
The default timeout DEFAULT_STATUS_TIMEOUT is globally adjustable:
>>> from crl.interactivesessions.shells.shell import ( ... DefaultStatusTimeout, ... DEFAULT_STATUS_TIMEOUT, ... Shell) >>> >>> class ExampleShell(Shell): ... def get_start_cmd(self): ... pass ... def start(self): ... pass ... def exit(self): ... pass ... def get_status_code(self, timeout=DEFAULT_STATUS_TIMEOUT): ... return float(timeout) >>> >>> DefaultStatusTimeout.set(20) >>> ExampleShell().get_status_code() 20.0
-
start
()¶ Initialize the new shell.
This method will be called after running the command provided by get_start_cmd() and provides ability for the new shell to initialize itself.
-
KeyAuthenticatedSshShell¶
-
class
crl.interactivesessions.shells.keyauthenticatedsshshell.
KeyAuthenticatedSshShell
(host, initial_prompt, tty_echo=False)¶ Bases:
crl.interactivesessions.shells.sshshell.SshShell
This class can be used in order to start a remote Ssh shell with keyauthentication instead of password authentication. It requires that ssh keys are set before pushing the shell. See also
SshShell
.Args:
host: IP address or name of the host
- initial_prompt: check_start_success method requires prompt,
- status check command is sent when initial prompt is found, example:’$ ‘
tty_echo: If True, then terminal echo is set on.
-
check_start_success
()¶ check status after pushing KeyAuthenticatedSshShell raise ShellStartError if status is not 0
Sending something after receiving status output is needed because sshshell _common_start keyword expects not empty buffer after check_start_success.
-
get_start_cmd
()¶ Return the command to be run in order to start this shell.
This method will be called by InteractiveSession.spawn() and InteractiveSession.push().
-
wait_for_initial_prompt
()¶ read until prompt is coming raise ShellStartError if prompt is not found
-
exception
crl.interactivesessions.shells.keyauthenticatedsshshell.
ShellStartError
¶ Bases:
crl.interactivesessions.interactivesessionexceptions.InteractiveSessionError
SudoShell¶
-
exception
crl.interactivesessions.shells.sudoshell.
SudoError
¶ Bases:
crl.interactivesessions.interactivesessionexceptions.InteractiveSessionError
Raised when
SudoShell
start fails
-
class
crl.interactivesessions.shells.sudoshell.
SudoShell
(cmd='sudo bash', password=None)¶ Bases:
crl.interactivesessions.shells.bashshell.BashShell
Running commands under sudo.
For setting timeout for reading login banner, i.e. lecture, please use
msgreader.MsgReader.set_timeout()
.-
get_start_cmd
()¶ Return the command to be run in order to start this shell.
This method will be called by InteractiveSession.spawn() and InteractiveSession.push().
-
start
()¶ Initialize the new shell.
This method will be called after running the command provided by get_start_cmd() and provides ability for the new shell to initialize itself.
-
Shell Tools¶
The shells based on Shell can be registered via Registering Shells. The registered shells can be used in the shell stack container described in Shell Stack Container.
Registering Shells¶
-
class
crl.interactivesessions.shells.registershell.
RegisterShell
¶ Registration decorator for the
shell.Shell
based shells.Usage example:
@RegisterShell() class AliasBashShell(BashShell): pass
-
class
crl.interactivesessions.shells.registershell.
RegisteredShells
¶ Dictionary for
shell.Shell
based shell classes registered viaRegisterShell
.-
create_shell
(shellname, **kwargs)¶ Create shell instance with class name shellname and with keyword arguments kwargs.
-
get_shellcls
(shellclsname)¶ Return shell class for the name shellcls. Raises
UnregisteredShell
if not found.
-
-
exception
crl.interactivesessions.shells.registershell.
ShellAlreadyRegistered
¶ Raised when the shell is already registerd with
RegisterShell
.
-
exception
crl.interactivesessions.shells.registershell.
UnregisteredShell
¶ Raised when the shell name is not registered with
RegisterShell
.
Shell Stack Container¶
-
class
crl.interactivesessions.shells.shellstack.
DefaultSshShell
(**kwargs)¶ Alias for
InteractiveSession.SshShell
which converts the __init__ arguments in the following fashion:DefaultSshShell
- Argument
sshshell.SshShell
Argumenthost ip username or user username Other keyword arguments are passsed without any conversion to
sshshell.SshShell
.
-
class
crl.interactivesessions.shells.shellstack.
ShellStack
¶ This class is a stack of the
shell.Shell
based shells.-
initialize
(shelldicts)¶ Initialize shells with list of dictionaries, shelldicts, where each dictionary is mapped to registered
shell.Shell
based shells in the order of the list. The dictionary can contain the class name (shellname) of the registered shell. The rest of the name-value pairs are passed as keyword arguments to the associated shell class. If no shellname is given, thenDefaultSshShell
is created from the dictionary.
-
shells
¶ Shell stack created from shelldicts.
-
MsgReader¶
-
class
crl.interactivesessions.shells.msgreader.
MsgReader
(read_until_end)¶ MsgReader is for reading login banner e.g. ‘lecture’ or ‘message-of-day’ messages from the terminal in shells.
- Args:
- read_until_end:
shell.Shell._read_until_end()
-
classmethod
get_timeout
()¶ Get timeout.
-
read_until_end
()¶ Reads message and returns it. Timeout _timeout is used for reading timeout.
-
classmethod
reset_timeout
()¶ Reset timeout to original default value.
-
classmethod
set_timeout
(timeout)¶ Set timeout.
- Args:
- timeout: timeout in seconds