Tutorial

Learning guide for basic usage of rcontrol.

Executing a command on a remote host

To execute a command, you first need to create a session. A session is usually used inside a with block, to ensure that all tasks will finish and that the connection will be closed at the end.

from rcontrol.ssh import ssh_client, SshSession

# create a ssh connection. This basically create a connected
# paramiko.SSHClient instance.
conn = ssh_client('localhost', 'jp', 'jp')

# execute the command
with SshSession(conn) as session:
    session.execute("uname -a")

# outside the with statement, all tasks are done and the connection
# is automatically closed.

If you ran this snippet, you will see nothing on the screen. This is because there is no handler defined for the command output:

from rcontrol.ssh import ssh_client, SshSession

def on_finished(task):
    print("finished (exit code: %d) !" % task.exit_code())

def on_output(task, line):
    print("output: %s" % line)

conn = ssh_client('localhost', 'jp', 'jp')

with SshSession(conn) as session:
    session.execute("uname -a", on_stdout=on_output, on_finished=on_finished)

Output:

output: Linux JP-Precision-T1500 3.13.0-39-generic #66-Ubuntu SMP Tue Oct 28 13:30:27 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
finished (exit code: 0) !

See also

Sessions, Tasks.

Synchronizing commands

Here is an example of how to synchronize tasks. To run two commands in parallel, then wait for them to finish an run a last command after that:

from rcontrol.ssh import ssh_client, SshSession

conn = ssh_client('localhost', 'jp', 'jp')

with SshSession(conn) as session:
    # this will run in parallel
    task1 = session.execute("sleep 1; touch /tmp/rcontrol1.test")
    task2 = session.execute("sleep 1; touch /tmp/rcontrol2.test")

    # now wait for the commands to complete
    task1.wait()
    task2.wait()
    # or session.wait_for_tasks()

    # and do something else
    session.execute("rm /tmp/rcontrol{1,2}.test")
    # no need to wait for this task, it will be done automatically
    # since we are in the with block

Executing local commands

Local commands can be executed in the same way as remote ones. Just use a rcontrol.local.LocalSession:

from rcontrol.local import LocalSession

with LocalSession() as session:
   session.execute("touch /tmp/stuff")

Executing commands on multiple hosts

It is recommended to use a session manager to work with multiple hosts at the same time:

from rcontrol.ssh import SshSession, ssh_client
from rcontrol.core import SessionManager

with SessionManager() as sessions:
    # create sessions
    sessions.bilbo = SshSession(
        ssh_client('http://bilbo.domain.com', 'user', 'pwd'))
    sessions.nazgul = SshSession(
        ssh_client('http://nazgul.domain.com', 'user', 'pwd'))

    # run commands in parallel
    sessions.bilbo.execute("someLongCommand")
    sessions.nazgul.execute("anotherCommand")

    # wait for these commands to finish, then run a last one
    sessions.wait_for_tasks()

    sessions.nazgul.execute("echo 'Done !'")

More on commands synchronisation

Let’s say we have to execute some commands on multiple hosts:

T1, T2, T3 will be started at the same time. Once T1 is finished, T11 and T12 tasks must be started. Once T11, T12 and T2 are finished, T4 must be started. Finally, we can start T5 once T4 and T3 are finished.

T1, T11, T12, T5 must be executed on atlas2.

T2, T4 must be executed on bilbo.

T3 must be executed on nazgul.

digraph graphname {
    BEGIN -> "atlas2:T1";
    BEGIN -> "bilbo:T2";
    BEGIN -> "nazgul:T3";
    "atlas2:T1" -> "atlas2:T11";
    "atlas2:T1" -> "atlas2:T12";
    "atlas2:T11" -> "bilbo:T4";
    "atlas2:T12" -> "bilbo:T4";
    "bilbo:T2" -> "bilbo:T4";
    "bilbo:T4" -> "atlas2:T5";
    "nazgul:T3" -> "atlas2:T5";
    "atlas2:T5" -> END;

    { rank=same; "atlas2:T1" "bilbo:T2" "nazgul:T3" }
}

Here is a possible implementation:

from rcontrol.ssh import SshSession, ssh_client
from rcontrol.core import SessionManager

def show(task, line):
    LOG.info('%s: %s', task.session, line)

with SessionManager() as sessions:
    # create sessions
    sessions.atlas2 = SshSession(
        ssh_client('http://atlas2.domain.com', 'user', 'pwd'))
    sessions.bilbo = SshSession(
        ssh_client('http://bilbo.domain.com', 'user', 'pwd'))
    sessions.nazgul = SshSession(
        ssh_client('http://nazgul.domain.com', 'user', 'pwd'))

    def sub_build(task):
        task.session.execute("echo 'task 11'", on_stdout=show)
        task.session.execute("echo 'task 12'", on_stdout=show)

    sessions.atlas2.execute("echo 'task 1'", on_finished=sub_build, on_stdout=show)
    sessions.bilbo.execute("echo 'task 2'", on_stdout=show)
    sessions.nazgul.execute("echo 'task 3'", on_stdout=show)

    # wait for tasks on atlas2 and bilbo
    # note that the build 3 task on nazgul still run in the background
    sessions.atlas2.wait_for_tasks()
    sessions.bilbo.wait_for_tasks()

    # now run another build
    sessions.bilbo.execute("echo 'task 4'", on_stdout=show)

    # wait for task 3 and 4 (all active tasks)
    sessions.wait_for_tasks()

    # and finally run a last task
    sessions.atlas2.execute("echo 'task 5'", on_stdout=show)

Note

In this example, errors are not handled. If an error occurs during a task execution, following tasks won’t be executed and the error(s) will be raised as soon as possible.

Copy files and directories between hosts

Here is an example that show how to copy files and directories accros computer. Note that you can use the rcontrol.local.LocalSession to get or put files and directories locally.

from rcontrol.ssh import SshSession, ssh_client
from rcontrol.core import SessionManager

with SessionManager() as sessions:
    # create sessions
    sessions.bilbo = SshSession(
        ssh_client('http://bilbo.domain.com', 'user', 'pwd'))
    sessions.nazgul = SshSession(
        ssh_client('http://nazgul.domain.com', 'user', 'pwd'))

    # copy a file on nazgul, block until it is done
    sessions.bilbo.s_copy_file('/tmp/stuff', sessions.nazgul, '/tmp/stuff')

    # copy recursive dirs in a non blocking way (you can synchronize it just
    # like commands)
    # Note that the destination folder /tmp/dir on nazgul must not exists
    sessions.bilbo.copy_dir('/home/my/dir', sessions.nazgul, '/tmp/dir')