Shremote!
Execute commands remotely over SSH!
Usage: python shremote.py <config.yml> <label> [--test] [--export <loc>] [--stop-only]
--test
: Runs through each command as fast as possible to ensure proper formatting--export
: Copies all log files to at end of test--stop-only
: Stops all programs present in the config
I think the program is easier to understand from examples, so be sure to look at the config and sample yml files.
Configuration
The following is a sample configuration file. Details of each section are laid out below:
# Declare the default ssh parameters
ssh:
user: isaac-ped
key: '~/.ssh/id_rsa'
port: 22
# These commands are run on the local machine before test begins
# Use it to generate files used during testing
init_cmds:
generate_client_file: 'python make_client_file.py --output {0.files.client_file.src}'
# Copy files from the local machine to remote hosts
files:
client_file:
src: 'my_test_file.dat'
dst: '{0.programs.log_dir}/my_test_file.dat'
host: clients
# Hosts are machines used in the experiments
hosts:
# The can consist of a single machine:
server:
addr: domain1.edu
# Or a group of machines:
clients:
- addr: domain2.edu
- addr: domain3.edu
ssh: # And can provide custom ssh parameters
user: domain3_user
key: domain3_key
port: 22
# Not necessary, but can declare parameters here referencable from anywhere
globals:
experiment_length: 60
my_param: 'parameter_value'
# Next, define the programs that will be executed during the test
programs:
# Programs all write logs to this base directory
log_dir: ~/logs/{0.label}/
# This program runs on the 'server' host
setup_exp:
host: server
start: 'cd /path/to/script && ./do_something {param_1} {other_param}'
stop: pkill do_something
check_rtn: 0 # Ensure the processs returns 0
log: # Automatically log all stdout and stderr to these files
out: setup_stdout.txt
err: setup_stderr.txt
run_exp:
host: clients
start: 'cd /path/to/script && ./run_experiment {param_2} {duration}'
stop: pkill run_experiment
log: # Put logs in this subdirectory
dir: experiment
out: experiment_{i}_stdout.txt # Label logs with the experiment number
err: experiment_{i}_stderr.txt
# Finally, define the timecourse and parameters of execution
commands:
setup_exp:
begin: -20 # Start 20 second before main experiment
param_1: '{0.args.setup_param}' # Get this parameter from startup args
other_param: '{0.globals.my_param}' # Get this one from global definitions
# Run the experiment two times, with a 60 second pause between
run_exp:
- begin: 0
duration: '{0.globals.experiment_length}' # Run for 60 seconds
enforce_duration: True # Make sure that it doesn't end early
param_2: '{0.args.exp_param}' # Get this from startup args
- begin: '{0.globals.experiment_length} * 2' # Math possible!
duration: '{0.globals.experiment_length}'
param_2: '{0.args.exp_param}'
The programs, commands, logs, and timing are all defined in a central yml file.
For ease of reproducing, a special !include <file>
command is added to the yml parser.
This allows portions of the config used across multiple files to be kept in a centralized location.
Throughout the config file, different parts of the config can be referenced using python's
string formatting syntax. The complete config is passed as the first argument to all formatting,
such that if one defines foo: bar
at the top level of the config, qux: {0.foo}
will change to
qux: bar
after substitution.
Certain special variables are also available via substitution, such as {0.label}
, which is
the label passed in at runtime. Different parts of the config have their own variables
(described below).
The required parts of the config file are:
ssh:
The default SSH configuration by which to connect to the hosts. Must define:
user
: The username with which to connectkey
: A path to an private SSH-keyport
: The port over which to connect
hosts:
A list of hosts on which programs can be run. Each host defines:
addr
: The hostname or IP address at which the host can be reachedssh
: Optional SSH config (as above) which overrides the default config
Note: A "host" may actually be a list of hosts, in which case commands that specifies that host will be run on all specified machines
logs:
A dictionary mapping program names to log files. Log files will be rsynced locally at the end of the session.
This section must define:
dir
: The directory on each host to which log files will be recorded. Will be created if it does not exist.
In addition, a log file may be specified for each program (defined below). Each log entry can specify:
dir
: A subdirectory into which logs will be placedout
: Redirects stdout to this fileerr
: Redirects stderr to this filelog
: Can be passed (fully formatted) to the program's execution string
Each log has available the variable:
{i}
: If a program is run multiple times, or on multiple hosts, this index will distinguish log files from one another. (Behavior undefined if{i}
is not used and multiple program instances are started){host}
: Will substitute the address of the host on which the command is being run
programs:
This section defines a dictionary mapping each program name to the command used to execute that program.
Each program has the following required fields:
host
: The name of the host (as defined inhosts:
above) on which the program should be run. Note: as stated above, a "host" may be a list of hosts on which the program will run simultaneously.start
: (Not strictly required ifstop
is defined) The command used to start the program. In addition to referencing global config variables (with{0.field}
), it can also reference:{var}
wherevar:
is defined in the instance of the program (defined below).{log}
which is substituted for the log filename as defined in thelogs:
config portion
The following optional fields:
stop
: The command used to stop execution of the program. Used ifduration
is present in the command arguments, or--stop-only
is passed to the python program as a whole.fg
: IfTrue
, will execute the program in the foreground (and block execution till completion) rather than spawning a thread. Should only be used for short-lived or blocking commands.check_rtn
: If present, will stop execution if the return code of the program run doesn't match the value provided here. To verify correct execution of most programs, specify 0.
dirs:
This is not required at all. This is just a place to put commonly used directories, and
must be referred to with {0.dirs.<dir>}
init_cmds:
A list of commands to be run locally before running anything remotely. Can be useful if, e.g., a file must be generated which depends on some program execution's parameters.
files:
A list of files to be copied to remote hosts at the start of shremote's execution
commands:
The meat of the config!
A dictionary mapping the program instances to their arguments and the times at which they start.
Each command name should match a program
(and optionally a log
) as defined above.
Each command has the following fields, all optional:
begin
: The time (in seconds) at which to start the command. Can include references and math. (e.g.{0.experiment_duration} + 5
). Can be negative, to enforce commands running earlier. (defaults to 0).duration
: The duration for which the command should be executed (in seconds). Thestop
part of the program's definition will be run at this time, if present.enforce_duration
: If present andTrue
, will halt execution of the shremote session if the program runs for less long than specified.var
: Wherevar
is used in the program'sstart
orstop
definitions.