Welcome to Glusto’s documentation!¶
Glusto is a framework designed to provide features commonly used in a remote/distributed environment via a single and easy-to-access object.
It started out as a port of some shell ssh functions I had written and was meant for use in PyUnit* tests and config scripts for Gluster.
I’ve removed the Gluster specifics from this package. Feel free to give it a go, and please let me know how it works out.
Some of the key concepts and features of Glusto:
- Glusto inherits from multiple classes providing configuration (yaml, json, ini), remote connection (SSH, SCP, RPyC), ANSI color output, logging, and unit test functionality (PyUnit, PyTest, Nose)–presenting them in a single global Class object.
- Glusto also acts as a global class for maintaining state and configuration data across multiple modules and classes.
- Glusto provides a wrapper utility (
/usr/bin/glusto
) to help make configuration files available to test cases from the command-line.
Adding Glusto utilities to a Python module is as simple as an import.
- Example:
To use Glusto in a module:
from glusto.core import Glusto as g
Note
It is no longer necessary to say “Glusto Importo!” out loud before executing scripts using the Glusto module. The import statement is more than sufficient.
User Guide¶
Table of Contents¶
Installing Glusto¶
There is more than one way to install Glusto.
- Installing the package directly from the github repo via the
pip
command. - Installing a docker image from Docker Hub.
- Cloning from github and installing via setuptools.
Installing Glusto Directly from Git via Pip¶
The pip
command can install directly from the Glusto project repo on github.com.
# pip install --upgrade git+git://github.com/loadtheaccumulator/glusto.git
Using Glusto via Docker¶
A minimal Docker image is available to download and experiment with Glusto.
Note
The image has currently been tested on Fedora 23 and Mac OS X (El Capitan) running Docker for Mac without issues.
To use the Glusto Docker image, pull the image from Docker Hub and go.
docker pull loadtheaccumulator/glusto docker run -it --rm loadtheaccumulator/glusto /bin/bash
This takes you into the running container as root. Please reference the documentation on docker.com (or available all over the web now) for more information on using Docker.
Note
You will need to pay particular attention to keys and configs when using the docker image. It might be useful to create a Dockerfile to build a new image, based on the Glusto image, that makes your own custom config, keys, and tests available. The Dockerfile used to create the Glusto image is available in the GitHub repo, so you can also just roll your own on the distro image of your choice. More on Docker later, but for now… experiment.
Cloning the Glusto Github Repo¶
On the system where Glusto is to be installed, clone the repo…
# git clone https://github.com/loadtheaccumulator/glusto.git
Installing Glusto from a Git Clone¶
To install the Glusto package via setuptools.
Change directory into the glusto directory.
# cd glusto
Run the setuptools script.
# python setup.py
Configuring Glusto¶
Glusto currently reads configuration files in yaml, json, or ini format.
It looks in /etc/glusto
for defaults.yml
, defaults.yaml
, defaults.json
and defaults.ini
.
You can provide any or all at the same time.
Note
It is currently necessary to create the /etc/glusto
directory manually
and populate it with defaults.
files. Automatic creation of the defaults
directory, a default defaults.yml
, and sample configs is upcoming.
defaults.yml or defaults.yaml:
keyfile: "~/ssh/id_rsa"
use_ssh: True
use_controlpersist: True
log_color: True
defaults.ini:
[defaults]
this = yada1
that = yada2
the_other = %(this)s and %(that)s
[globals]
some_default = yada yada
defaults.json:
{"things": {
"thing_one": "yada",
"thing_two": "yada yada",
"thing_three": {
"combo_thing": [
{"combo_thing_one": "yada", "combo_thing_two": "yada yada"}
]
}
}}
The ini format provides some simple variable capability.
For example, this line from the above defaults.ini config:
the_other = %(this)s and %(that)s
…will populate the_other variable in your Python script as “yada1 and yada2”:
defaults: {that: yada2, this: yada1, this_and_that: yada1 and yada2}
Note
It is also possible to pass additional configuration files at the command-line, in IDLE, or from within script.
via the -c
option. See Using Config Files with Glusto and
Using the Glusto CLI Utility for more information.
Using Glusto¶
Using Glusto in a Module¶
To use Glusto in a module, import the Glusto class at the top of each module leveraging the glusto tools.
from glusto.core import Glusto as g
This provides access to all of the functionality of Glusto via the g
object
created by the import statement.
Using Glusto via the Python Interactive Interpreter¶
One of the primary objectives of Glusto is to maintain feature support from the Python Interactive Interpreter. Most, if not all, features should be easily referenced via the interpreter to ease use and reduce time during development.
To use Glusto via the Python Interactive Interpreter, enter the interpreter via
the python
command.
$ python >>> from glusto.core import Glusto as g
This provides access to all of the functionality of Glusto via the g.
object
created by the import statement–in the same way as imported in a script.
For example:
$ python >>> from glusto.core import Glusto as g >>> g.run_local('uname -a') (0, 'Linux mylaptop 4.4.9-300.fc23.x86_64 #1 SMP Wed May 4 23:56:27 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux\n', '') >>> config = g.load_config('examples/systems.yml') >>> config {'nodes': ['192.168.1.221', '192.168.1.222', '192.168.1.223', '192.168.1.224'], 'clients': ['192.168.1.225'], 'masternode': '192.168.1.221'} >>> g.run(config['nodes'][0], 'uname -a') (0, 'Linux rhserver1 2.6.32-431.29.2.el6.x86_64 #1 SMP Sun Jul 27 15:55:46 EDT 2014 x86_64 x86_64 x86_64 GNU/Linux\n', '') >>> g.list_ssh_connections() root@192.168.1.221 >>> g.run_serial(config['nodes'], 'hostname') {'192.168.1.224': (0, 'rhserver4\n', ''), \ '192.168.1.221': (0, 'rhserver1\n', ''), \ '192.168.1.223': (0, 'rhserver3\n', ''), \ '192.168.1.222': (0, 'rhserver2\n', '')} >>> g.list_ssh_connections() root@192.168.1.222 root@192.168.1.223 root@192.168.1.221 root@192.168.1.224
Typing from glusto.core import Glusto as g
each time you start the
interpreter can become tedious. To automatically run commands, the PYTHONSTARTUP
environment variable can be pointed to a python script containing common setup commands.
$ cat examples/pythonstartup_script.py from glusto.core import Glusto as g print "Python startup starting" config = g.load_config('examples/systems.yml') rcode, rout, rerr = g.run(config['nodes'][0], 'uname -a') print ('The uname info is: %s' % rout) print "Python startup complete"$ export PYTHONSTARTUP=examples/pythonstartup_script.py $ python Python 2.7.11 (default, Mar 31 2016, 20:46:51) [GCC 5.3.1 20151207 (Red Hat 5.3.1-2)] on linux2 Type "help", "copyright", "credits" or "license" for more information. Python startup starting Python startup complete >>> g <class 'glusto.core.Glusto'> >>> config {'nodes': ['192.168.1.221', '192.168.1.222', '192.168.1.223', '192.168.1.224'], 'clients': ['192.168.1.225'], 'masternode': '192.168.1.221'} >>> uname_info 'Linux rhserver1 2.6.32-431.29.2.el6.x86_64 #1 SMP Sun Jul 27 15:55:46 EDT 2014 x86_64 x86_64 x86_64 GNU/Linux\n'
Using the Glusto CLI Utility¶
Glusto provides a wrapper utility for features like unit test support, etc. Currently, the Glusto CLI allows leveraging the PyUnit, PyTest, and Nose module features in an easily configurable and callable wrapper.
To see the options available, use the --help
option.
$ /usr/bin/glusto --help Starting glusto via main() usage: glusto [-h] [-c CONFIG_LIST] [-u] [-d DISCOVER_DIR] Glusto CLI wrapper optional arguments: -h, --help show this help message and exit -c CONFIG_LIST, --config CONFIG_LIST Config file(s) to read. -u, --unittest Run unittests per provided config file. -d DISCOVER_DIR, --discover DISCOVER_DIR Discover unittests from directory -t RUN_PYTEST, --pytest RUN_PYTEST Run tests using the pytest framework -n RUN_NOSETESTS, --nosetests RUN_NOSETESTS Run tests using the nose framework
By default, the glusto
command will read the default config files in the /etc/glusto/
directory
For example, this run of the command reads the
defaults.yml
anddefaults.ini
files in/etc/glusto/
:$ glusto Starting glusto via main() defaults: {that: yada2, the_other: yada1 and yada2, this: yada1} globals: {some_default: yada yada} keyfile: ~/ssh/id_rsa log_color: true that: yada2 the_other: yada1 and yada2 this: yada1 use_controlpersist: true use_ssh: true Ending glusto via main()
Options for Running Unit Tests¶
To run unit tests via the Glusto CLI Utility, see the examples and links to additional documentation below.
Example:
$ glusto -c 'examples/systems.yml' -u -d 'tests' $ glusto -c 'examples/unittests/unittest.yml examples/unittests/unittest_list.yml examples/systems.yml' -u
For more information on working with unit tests, see Unittests and Glusto
Example:
$ glusto -c 'examples/systems.yml' --pytest='-v -x tests -m response'
For more information on working with unit tests, see PyTest and Glusto
Example:
$ glusto -c 'examples/systems.yml' --nosetests='-v -w tests'
For more information on working with unit tests, see Nose and Glusto
Not that the need would arise, but the capability to run all three in a single command is there.
Example running tests with
--pytest=
andnosetests=
options:$ glusto -c 'examples/systems.yml examples/unittests/unittest.yml' -u --nosetests='-w tests' --pytest='-x tests -m response' Starting glusto via main() clients: [192.168.1.225] masternode: 192.168.1.221 nodes: [192.168.1.221, 192.168.1.222, 192.168.1.223, 192.168.1.224] unittest: load_tests_from_module: {module_name: tests.test_glusto, use_load_tests: true} output_junit: false clients: [192.168.1.225] masternode: 192.168.1.221 nodes: [192.168.1.221, 192.168.1.222, 192.168.1.223, 192.168.1.224] unittest: load_tests_from_module: {module_name: tests.test_glusto, use_load_tests: true} output_junit: false PREFIX: tests.test_glusto.TestGlustoBasics Setting Up Class: TestGlustoBasics test_return_code (tests.test_glusto.TestGlustoBasics) Testing the return code ... Setting Up: tests.test_glusto.TestGlustoBasics.test_return_code Running: tests.test_glusto.TestGlustoBasics.test_return_code - Testing the return code Tearing Down: tests.test_glusto.TestGlustoBasics.test_return_code ok test_stdout (tests.test_glusto.TestGlustoBasics) Testing output to stdout ... Setting Up: tests.test_glusto.TestGlustoBasics.test_stdout Running: tests.test_glusto.TestGlustoBasics.test_stdout - Testing output to stdout Tearing Down: tests.test_glusto.TestGlustoBasics.test_stdout Cleaning up after setup on fail or after teardown ok test_stderr (tests.test_glusto.TestGlustoBasics) Testing output to stderr ... Setting Up: tests.test_glusto.TestGlustoBasics.test_stderr Running: tests.test_glusto.TestGlustoBasics.test_stderr - Testing output to stderr Tearing Down: tests.test_glusto.TestGlustoBasics.test_stderr ok test_expected_fail (tests.test_glusto.TestGlustoBasics) Testing an expected failure. This test should fail ... Setting Up: tests.test_glusto.TestGlustoBasics.test_expected_fail Running: tests.test_glusto.TestGlustoBasics.test_expected_fail - Testing an expected failure. This test should fail expected failure Tearing Down: tests.test_glusto.TestGlustoBasics.test_expected_fail test_negative_test (tests.test_glusto.TestGlustoBasics) Testing an expected failure as negative test ... Setting Up: tests.test_glusto.TestGlustoBasics.test_negative_test Running: tests.test_glusto.TestGlustoBasics.test_negative_test - Testing an expected failure as negative test Tearing Down: tests.test_glusto.TestGlustoBasics.test_negative_test ok test_skip_me (tests.test_glusto.TestGlustoBasics) Testing the unittest skip feature ... skipped 'Example test skip' Tearing Down Class: TestGlustoBasics ---------------------------------------------------------------------- Ran 6 tests in 0.585s OK (skipped=1, expected failures=1) pytest: -x tests -m response ==================================================================================== test session starts ===================================================================================== platform linux2 -- Python 2.7.11, pytest-2.9.2, py-1.4.31, pluggy-0.3.1 rootdir: glusto, inifile: collected 21 items tests/test_glusto_pytest.py ... =========================================================================== 18 tests deselected by "-m 'response'" =========================================================================== ========================================================================== 3 passed, 18 deselected in 0.32 seconds =========================================================================== nosetests: -w tests /usr/lib64/python2.7/unittest/case.py:378: RuntimeWarning: TestResult has no addExpectedFailure method, reporting as passes RuntimeWarning) ...S..E...F.....E...... ====================================================================== ERROR: Load tests in a specific order. ---------------------------------------------------------------------- Traceback (most recent call last): File "/usr/lib/python2.7/site-packages/nose/case.py", line 197, in runTest self.test(*self.arg) TypeError: load_tests() takes exactly 3 arguments (0 given) ====================================================================== ERROR: Load tests in a specific order. ---------------------------------------------------------------------- Traceback (most recent call last): File "/usr/lib/python2.7/site-packages/nose/case.py", line 197, in runTest self.test(*self.arg) TypeError: load_tests() takes exactly 3 arguments (0 given) ====================================================================== FAIL: Testing an expected failure. This test should fail ---------------------------------------------------------------------- Traceback (most recent call last): File "glusto/tests/test_glusto_pytest.py", line 98, in test_expected_fail self.assertEqual(rcode, 0) AssertionError: 1 != 0 -------------------- >> begin captured stdout << --------------------- Setting Up: tests.test_glusto_pytest.TestGlustoBasicsPyTest.test_expected_fail Running: tests.test_glusto_pytest.TestGlustoBasicsPyTest.test_expected_fail - Testing an expected failure. This test should fail --------------------- >> end captured stdout << ---------------------- -------------------- >> begin captured logging << -------------------- plumbum.local: DEBUG: Running ['/usr/bin/ssh', '-T', '-oPasswordAuthentication=no', '-oStrictHostKeyChecking=no', '-oPort=22', '-oConnectTimeout=10', '-oControlMaster=auto', '-oControlPersist=4h', '-oControlPath=~/.ssh/glusto-ssh-%r@%h:%p', 'root@192.168.1.221', 'cd', '/root', '&&', 'false'] --------------------- >> end captured logging << --------------------- ---------------------------------------------------------------------- Ran 23 tests in 1.964s FAILED (SKIP=1, errors=2, failures=1) Ending glusto via main()
Note
I’ll be able to demonstrate this better when I have PyTest example test scripts written. The above command runs the same PyUnit-based test scripts against the PyUnit, PyTest and Nose frameworks.
Working With Remote Systems¶
Glusto provides functions for running commands on remote systems, as well as sending and retrieving files.
Passwordless SSH with Keys¶
Glusto relies on existing SSH keys. Please consult the docs for your specific platform for more information on how to setup passwordless ssh.
Configuring Glusto to Use Specific Keys¶
Add a specific SSH key to the /etc/glusto defaults configs…
For example, add the following line to /etc/glusto/defaults.yml
:
keyfile: "~/ssh/id_rsa"
Run a Single Command via SSH¶
To run a command on a remote system via SSH, use the “run” command:
>>> g.run('server01.example.com', 'uname -a')
(0, 'Linux server01 2.6.32-431.29.2.el6.x86_64 #1 SMP Sun Jul 27 15:55:46 EDT 2014 x86_64 x86_64 x86_64 GNU/Linux\n', '')
It is also easy enough to assign the return code, stdout, and stderr to variables:
>>> retcode, stdout, stderr = g.run('server01.example.com', 'uname -a')
>>> retcode
0
>>> stdout
'Linux server01 2.6.32-431.29.2.el6.x86_64 #1 SMP Sun Jul 27 15:55:46 EDT 2014 x86_64 x86_64 x86_64 GNU/Linux\n'
>>> stderr
''
Run a Single Command on the Localhost¶
A command can be run on the localhost via SSH by simply passing ‘localhost’ as the hostname. As a convenience, and to save some overhead, Glusto provides a method to run a command locally and get the return code, stdout, and stderr like the remote run command.
To run a command on the local system:
>>> g.run_local('uname -a')
(0, 'Linux localhost 4.4.9-300.fc23.x86_64 #1 SMP Wed May 4 23:56:27 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux\n', '')
Run a Command on More than One Host¶
Glusto provides convenience methods to run commands against multiple hosts.
Run a Command Serially on Multiple Servers¶
To run a command against a list of hosts, use the run_serial()
method.
The command will be run against the hosts one after another.
>>> hosts = ["breedshill.example.com", "bunkerhill.example.com"] >>> results = g.run_serial(hosts, 'uname -a')
Run a Command in Parallel¶
To run a command against a list of hosts in parallel, use the run_parallel()
method.
The command will be run against the hosts at the same time.
>>> command = "uname -a" >>> results = g.run_parallel(hosts, 'uname -a') {'192.168.1.221': (0, 'Linux rhserver1 2.6.32-431.29.2.el6.x86_64 #1 SMP Sun Jul 27 15:55:46 EDT 2014 x86_64 x86_64 x86_64 GNU/Linux\n', ''), '192.168.1.222': (0, 'Linux rhserver2 2.6.32-431.29.2.el6.x86_64 #1 SMP Sun Jul 27 15:55:46 EDT 2014 x86_64 x86_64 x86_64 GNU/Linux\n', '')}
Run a Command Asynchronously¶
The run_parallel
method is a convenience method and runs the same command against
a list of systems using the same user. It is possible to use the underlying
run_async
command directly to run a variety of combinations asynchronously.
An example of how run_parallel
uses run_async
:
>>> command = "uname -a"
>>> proc1 = g.run_async("bunkerhill", command)
>>> proc2 = g.run_async("breedshill", command)
>>> results1 = proc1.async_communicate()
>>> results2 = proc2.async_communicate()
To asynchronously run the same command against the same server as a different user:
>>> command = "uname -a; echo $USER"
>>> proc1 = g.run_async("breedshill", command, user="howe")
>>> proc2 = g.run_async("breedshill", command, user="pigot")
>>> results1 = proc1.async_communicate()
>>> results2 = proc2.async_communicate()
Note
run_async() runs commands asynchronously, but blocks on async_communicate() and reads output sequentially. This might not be a good fit for run-and-forget commands.
Transferring Files To and From Remote Systems¶
Glusto provides methods to call SShMachine’s upload and download commands, as well as a method to transfer a file directly between remote systems.
Uploading a File¶
To upload a file to a remote system, use the upload()
method.
>>> g.upload('server01.example.com', '/etc/localfile.txt', '/tmp/localfile_remotecopy.txt')
Downloading a File¶
To download a file from a remote system, use the download()
method.
>>> g.download('server01.examples.com', '/etc/remotefile.txt', '/tmp/remotefile_localcopy.txt')
Transferring a File from Remote to Remote¶
To transfer a file directly from a remote system to another remote system,
without having to first download to the local system and then upload to the remote,
use the transfer
method.
>>> g.transfer('server01.example.com', '/etc/remote1file.txt', 'server02.example.com', '/tmp/remote1file_remote2copy.txt')
Listing SSH Connections¶
To see a list of the current SSH connections, use the ssh_list_connections()
method.
>>> g.ssh_list_connections() root@192.168.1.222 root@192.168.1.223 root@192.168.1.221 root@192.168.1.224
Closing Connections¶
It is typically not necessary to close a connection. Connections are cached for quick re-use and SSH connections should close at program exit. Should the need arise…
Closing a Connection¶
To close a connection use the ssh_close_connection()
method.
>>> g.ssh_close_connection('192.168.1.221') >>> g.ssh_close_connection('192.168.1.221', user='george')
Close All Connections¶
To close all connections use the ssh_close_connections()
method.
>>> g.ssh_close_connections()
Using RPyC¶
Let’s start with this…
Warning
Per the install documentation for RPyC, it is not possible to connect to a Python 3.x remote from a Python 2.x system and vice-versa.
See the RPyC Install documentation [1] for more information. That’s not necessarily a show-stopper for everyone, but certainly worth consideration depending on your environment.
Passwordless Connections¶
Glusto’s implementation of RPyC leverages the same SSH connections described in the previous section. See Passwordless SSH with Keys for more information on configuring Glusto for specific SSH keys.
Setting up Connections¶
Unlike the SSH connections that are created automatically when you use run()
or the other SSH methods, the RPyC connection needs to be created before it can
be used. After the connection is made, it is cached for use by subsequent RPyC calls.
Setting up a Single Connection¶
To setup an RPyC connection to a remote server, use the rpyc_get_connection()
method.
>>> g.rpyc_get_connection('192.168.1.221')
Listing Connections¶
To see the list of connections, use the rpyc_list_connections()
method.
>>> g.rpyc_list_connections() root@192.168.1.221:1
Setting up a Connection with a Specific User¶
The default user is root. To setup a connection with a user other than the default,
add the user
parameter.
>>> g.rpyc_get_connection('192.168.1.221', user='george') >>> g.rpyc_list_connections() george@192.168.1.221:1
Setting up Multiple Connections with Different Users¶
Sometimes it is necessary to run commands as different users at the same time. With RPyC, it is possible to setup multiple connections to the same server with different users.
>>> g.rpyc_get_connection('192.168.1.221', user='george') >>> g.rpyc_get_connection('192.168.1.221', user='alexander') >>> g.rpyc_list_connections() alexander@192.168.1.221:1 george@192.168.1.221:1On the remote server, multiple instances of the rpyc server are run:
$ ps -ef | grep deployed george 7504 5456 0 18:13 ? 00:00:00 bash -c cd /home/george && /usr/bin/python2 /tmp/tmp.XuDwqQkXVq/deployed-rpyc.py george 7511 7504 1 18:13 ? 00:00:00 /usr/bin/python2 /tmp/tmp.XuDwqQkXVq/deployed-rpyc.py root 7579 3041 0 18:13 ? 00:00:00 bash -c cd /root && /usr/bin/python2 /tmp/tmp.xAfvVdtmjg/deployed-rpyc.py root 7582 7579 4 18:13 ? 00:00:00 /usr/bin/python2 /tmp/tmp.xAfvVdtmjg/deployed-rpyc.py
Setting up Multiple Connections with the Same User¶
There are also times when it is helpful to be able to run commands at the same time, but as the same user. For example, to run a long running command while checking another command running at the same time. With RPyC, it is possible to setup multiple connections to the same server with the the same user.
To setup a connection to the same server as the same user, use the instance
parameter to specificy an instance number.
>>> g.rpyc_get_connection('192.168.1.221') >>> g.rpyc_get_connection('192.168.1.221', instance=2) >>> g.rpyc_get_connection('192.168.1.221', user='george') >>> g.rpyc_get_connection('192.168.1.221', user='george', instance=2) >>> g.rpyc_list_connections() george@192.168.1.221:2 root@192.168.1.221:2 root@192.168.1.221:1 george@192.168.1.221:1
Note
Glusto doesn’t automatically increment the instance number. Specifying the same instance number will return the cached connection and not a new instance.
Making RPyC Calls¶
Note
Rather than cover RPyC in-depth here, below are some examples of using RPyC with Glusto. Please refer to the RPyC documentation [2] for more information.
Using the Connection¶
Once an RPyC connection is made, it can be referenced to make RPyC calls against the remote system.
conn1 = g.rpyc_get_connection('192.168.1.221') >>> conn1.modules.sys.platform 'linux2'
Asynchronous RPyC Calls¶
RPyC provides an asynchronous mechanism to allow for running remote calls in the background.
Sometimes you just want to kick off a process and let it run without needing to wait for it to finish or caring about the result.
To run a command in the background without waiting for a result.
>>> import rpyc >>> conn1 = g.rpyc_get_connection('192.168.1.221') >>> async_sleep1 = rpyc.async(conn1.modules.time.sleep) >>> async_sleep1(10) <AsyncResult object (pending) at 0x7f3382bc6f50>
Other times you want to wait for the processes to finish before continuing.
To wait for a backgrounded process, use the rpyc wait()
method.
>>> import rpyc >>> conn1 = g.rpyc_get_connection('192.168.1.221') >>> async_sleep1 = rpyc.async(conn1.modules.time.sleep) >>> res1 = async_sleep1(10) <AsyncResult object (pending) at 0x7f3382bc6530> >>> res1.wait()
When it is necessary to run a background command against a system and run another
command against the same system, you can use wait()
to wait for a return for
each call made.
>>> res1 = async_sleep(60)
>>> res2 = async_sleep(10)
>>> res2.wait()
>>> res1.wait()
Note
Because the backgrounded calls are made against the same connection, the
first call blocks the connection until complete. In the above example, the
res2.wait()
will block for 70 seconds. The res1.wait()
returns instantly.
To run multiple background calls against the same system, you can create a second connection and run the second background call against it.
>>> import rpyc >>> conn1 = g.rpyc_get_connection('192.168.1.221') >>> async_sleep1 = rpyc.async(conn1.modules.time.sleep) >>> conn2 = g.rpyc_get_connection('192.168.1.221', instance=2) >>> async_sleep2 = rpyc.async(conn2.modules.time.sleep) >>> res1 = async_sleep(60) >>> res = async_sleep(10) >>> res.wait() >>> res1 = async_sleep(60) >>> res2 = async_sleep2(10) >>> res2.wait() >>> res1.wait()
The first call will block on the first connection, while the second call runs in parallel on the other connection.
Running Local Code on the Remote System¶
Normally, a module already needs to reside on the remote system or be transferred at runtime to be called. Glusto leverages a feature of RPyC to define a local module on the remote system without the extra step of transferring a file into the remote PYTHONPATH.
This feature makes it simple to create module files of commonly used function, class, and method snippets for use on remote servers without the need to package, distribute, and install on each remote server ahead of time.
To define a local module on the remote system, use the rpyc_define_module()
method.
Local module script named
mymodule
with a function calledget_uname
:>>> import mymodule >>> connection = g.rpyc_get_connection('192.168.1.221') >>> r = g.rpyc_define_module(connection, mymodule) >>> r.get_uname() ('Linux', 'rhserver1', '2.6.32-431.29.2.el6.x86_64', '#1 SMP Sun Jul 27 15:55:46 EDT 2014', 'x86_64')
Going Ape with Monkey-Patching¶
Monkey-patching with RPyC can be a useful feature.
Monkey-patching Standard Out¶
While using the Python interpreter, it is sometimes helpful to be able to see the output of a call that is normally directed to stdout on the remote.
To wire the remote stdout to the local stdout…
>>> import sys >>> conn = g.rpyc_get_connection('192.168.1.221') >>> conn.modules.sys.stdout = sys.stdout >>> conn.execute("print 'Hello, World!'") Hello, World!
Re-wiring Local and Remote¶
Monkey-patching can be used to make lengthy or often-used remote calls appear local.
An oversimplified example:
# Monkey-patching the remote to a local object >>> conn = g.rpyc_get_connection('192.168.1.221') >>> r_uname = conn.modules.os.uname >>> r_uname() ('Linux', 'rhserver1', '2.6.32-431.29.2.el6.x86_64', '#1 SMP Sun Jul 27 15:55:46 EDT 2014', 'x86_64') # Calling the local uname method >>> import os >>> os.uname() ('Linux', 'mylaptop', '4.4.9-300.fc23.x86_64', '#1 SMP Wed May 4 23:56:27 UTC 2016', 'x86_64')A slightly better example:
>>> # create a function unaware of remote vs local >>> def collect_os_data(os_object): ... print os_object.uname() ... print os_object.getlogin() ... >>> # pass it the local object >>> collect_os_data(os) ('Linux', 'mylaptop', '4.4.9-300.fc23.x86_64', '#1 SMP Wed May 4 23:56:27 UTC 2016', 'x86_64') loadtheaccumulator >>> # pass it the remote object >>> collect_os_data(ros) ('Linux', 'rhserver1', '2.6.32-431.29.2.el6.x86_64', '#1 SMP Sun Jul 27 15:55:46 EDT 2014', 'x86_64') root
Checking Connections¶
To check a connection is still available, use the rpyc_ping_connection()
method.
>>> g.rpyc_ping_connection() connection is alive
Note
Pinging a connection gets the connection from cache, but if the connection was not established before the ping, it will be opened–followed by the ping.
Closing Connections¶
On occasion, it might be necessary to remove a connection from the cache (e.g., when a cached connection is no longer needed or when looping through connections to execute the same command against all connections and an unwanted connection is in the list).
Warning
Closing a connection directly without using the methods discussed in this section will leave a connection definition in the connection dictionary. You will want to close rpyc connections via these methods to avoid unnecessary cleanup. It will also guarantee any future features are handled correctly upon close.
Closing a Single Connection¶
To remove a cached connection, close it with the rpyc_close_connection()
method.
>>> g.rpyc_close_connection('192.168.1.221') >>> g.rpyc_list_connections() george@192.168.1.221:2 root@192.168.1.221:2 george@192.168.1.221:1 >>> g.rpyc_close_connection('192.168.1.221', user='george') >>> g.rpyc_list_connections() george@192.168.1.221:2 root@192.168.1.221:2 >>> g.rpyc_close_connection('192.168.1.221', user='george', instance=2) >>> g.rpyc_list_connections() root@192.168.1.221:2
Closing All Connections¶
To remove all cached connections, use the rpyc_close_connections()
method.
>>> g.rpyc_close_connections()
Undeploying the RPyC Server¶
With the RPyC Zero-Deploy automated setup, the RPyC server process running on the remote system does not stop when a connection is closed. To stop that process, it is necessary to close the deployed server connection setup by Zero-Deploy.
To list the deployed servers, use the rpyc_list_deployed_servers()
method.
>>> g.rpyc_list_deployed_servers() george@192.168.1.221 root@192.168.1.221 alexander@192.168.1.221
Note
When multiple connection instances to the same server with the same user exist, they share the same deployed server, so only one deployed server will appear in the list.
To close a deployed server connection, use the rpyc_close_deployed_server()
method.
>>> g.rpyc_list_deployed_servers() george@192.168.1.221 root@192.168.1.221 >>> g.rpyc_close_deployed_server('192.168.1.221', user='george') >>> g.rpyc_list_deployed_servers() root@192.168.1.221
Note
Glusto will automatically close all of the connection instances related to the deployed server being closed. However, it does not dispose of the cached SSH connection.
To close all deployed servers, use the rpyc_close_deployed_servers()
method.
>>> g.rpyc_close_deployed_servers()
Note
Glusto leverages the RPyC Zero-Deploy methodology which copies the RPyC
server files to the remote and sets up the SSH tunnel automatically.
This can add overhead when the first g.rpyc_get_connection()
call to a
remote server is made. The time lag is negligible on the LAN or short
distances across the WAN, but when dealing with a large number of systems
across the globe, especially on a slow link (DSL, etc), there may be lengthy
“go get something to drink” periods of time. Try it out and adjust according to your taste.
Footnotes
[1] | https://rpyc.readthedocs.io/en/latest/install.html#cross-interpreter-compatibility |
[2] | http://rpyc.readthedocs.io/en/latest/index.html |
Using Config Files with Glusto¶
Glusto currently supports loading and storing configs in YAML, INI, and JSON format. File format can be specified explicitly or Glusto can determine the format based on file extension.
Loading Config Files¶
Config files can be loaded from the local filesystem as well as URLs.
Loading Config Files From the Local Filesystem¶
To load configuration from a file, use the load_config()
method.
Example config file
examples/systems.yml
:$ cat examples/systems.yml clients: [192.168.1.225] masternode: 192.168.1.221 nodes: [192.168.1.221, 192.168.1.222, 192.168.1.223, 192.168.1.224]Example
load_config()
:>>> config = g.load_config('examples/systems.yml') >>> config {'nodes': ['192.168.1.221', '192.168.1.222', '192.168.1.223', '192.168.1.224'], 'clients': ['192.168.1.225'], 'masternode': '192.168.1.221'}
The config
dictionary object now contains Python object representations of the config in the file.
Loading Config Files from a URL¶
To load configuration from a URL, pass the load_config()
method a filename
beginning with http://
, https://
, or file://
.
>>> config = g.load_config('http://myserver.com/example.yaml')
Setting the Glusto Config Dictionary with a Config File¶
Glusto stores configs in a dictionary object named config
at the root of the Glusto class.
Using the set_config()
method will assign a loaded configuration to the Glusto config
class attribute.
The config will be available in any module where the Glusto class is imported.
Adding some data to demonstrate the effects of using
set_config()
:>>> g.config['this'] = 'yada' >>> g.config {'this': 'yada'}Example of using the
set_config()
method:>>> config = g.load_config('examples/systems.yml') >>> g.set_config(config) >>> g.config {'nodes': ['192.168.1.221', '192.168.1.222', '192.168.1.223', '192.168.1.224'], 'clients': ['192.168.1.225'], 'masternode': '192.168.1.221'}
The Glusto class attribute g.config
is now populated with the configuration loaded from file,
and the this
dictionary item is no longer there.
Warning
This is destructive. Any existing data in the g.config
attribute will be overwritten by the data passed to set_config()
.
Updating the Glusto Config Dictionary with a Config File¶
Updating with the update_config
method is similar to using set_config
,
but will add to the config and not overwrite everything in the config
class attribute automatically.
Adding some data to demonstrate the effects of using
update_config()
:>>> g.config['this'] = 'yada' >>> g.config {'this': 'yada'}Example of using the
update_config()
method:>>> config = g.load_config('examples/systems.yml') >>> g.update_config(config) >>> g.config {'this': 'yada','nodes': ['192.168.1.221', '192.168.1.222', '192.168.1.223', '192.168.1.224'], 'clients': ['192.168.1.225'], 'masternode': '192.168.1.221'}
With update_config()
, the this
dictionary item is still there.
To organize different configs in the g.config
dictionary, you can leverage
Python’s ability to have nested dictionaries.
Example:
g.config['systems'] = {} g.config['myapp'] = {}
Warning
When using nested dictionaries to separate different configs under the same
g.config
dictionary, as mentioned above, you will need to use update_config()
instead of set_config() as described in the Setting the Glusto Config Dictionary with a Config File section.
Displaying Objects in Config File Format¶
To output objects to stdout in config file format, use the show_config()
method.
>>> g.show_config(g.config) clients: [192.168.1.225] masternode: 192.168.1.221 nodes: [192.168.1.221, 192.168.1.222, 192.168.1.223, 192.168.1.224]
Storing Objects in Config File Format¶
Glusto provides a simple interface for formatting objects and storing them in a config file.
To format and store an object in a file, use the store_config()
method.
>>> g.config {'this': 'yada', 'nodes': ['192.168.1.221', '192.168.1.222', '192.168.1.223', '192.168.1.224'], 'clients': ['192.168.1.225'], 'masternode': '192.168.1.221'} >>> g.store_config(g.config, filename='/tmp/glusto_config.yml')$ cat /tmp/glusto_config.yml clients: [192.168.1.225] masternode: 192.168.1.221 nodes: [192.168.1.221, 192.168.1.222, 192.168.1.223, 192.168.1.224] this: yada
The store_config()
method will determine the config format based on the filename extension passed to it.
If a format needs to be specified (maybe the extension does not represent the format),
the format can be specified with the config_type
parameter.
>>> g.store_config(g.config, filename='/tmp/glusto_config.conf, config_type='ini')
Note
Glusto currently defaults to yaml format.
Creating an INI Config Format Compatible Object¶
The INI format is simple in layout with a section header followed by key=value pairs. For that reason, an object being stored in INI format needs to be a dictionary (or dictionaries) of key:value dictionaries.
>>> config = {'section1': {'this': 'yada', 'that': 'yada yada'}, 'section2': {'the_other': 'yada yada yada'}} >>> config {'section2': {'the_other': 'yada yada yada'}, 'section1': {'this': 'yada', 'that': 'yada yada'}}
To store the INI formatted object, pass it to the store_config()
method.
>>> g.store_config(config, filename='/tmp/config.ini')$ cat /tmp/config.ini [section2] the_other = yada yada yada [section1] this = yada that = yada yada
Note
Due to the nature of Python not maintaining order in certain objects, the order of the sections may not be the order in the dictionary being passed. To maintain section order, you will need to use an OrderedDict.
To store the INI formatted object with the sections in a specific order,
pass it to the store_config()
method as an OrderedDict object.
>>> from collections import OrderedDict >>> config = OrderedDict() >>> config.update('section1': {'this': 'yada'}) >>> config.update('section2': {'that': 'yada yada'}) >>> config.update('section3': {'the_other': 'yada yada yada'}) >>> g.store_config(config, '/tmp/ordered.ini')$ cat /tmp/ordered.ini [section1] this = yada [section2] that = yada yada [section3] the_other = yada yada yada
Loading Config from a String¶
YAML formatted text can be converted into a dictionary object using the load_yaml_string()
method.
>>> g.load_yaml_string(yaml_string) {'clusters': ['e2effa75a5a50560c3250b67cf71b465']}
JSON formatted text can be converted into a dictionary object using the load_json_string()
method.
>>> config = g.load_json_string(json_string) >>> config {u'clusters': [u'e2effa75a5a50560c3250b67cf71b465']}
Note
There is not a current method for loading an INI formatted string.
Adding Simple Configuration Capability to Your Own Class¶
Glusto provides an inheritable class (Intraconfig
) that can add basic introspection and config functionality to classes in your scripts.
Making a Class Configurable¶
Making a class configurable is as simple as making it inherit from the Intraconfig class.
To inherit from the Intraconfig, add Intraconfig
to the class definition.
Example making the class MyClass configurable:
>>> from glusto.configurable import Intraconfig >>> class MyClass(Intraconfig): >>> def __init__(self): >>> self.this = 'yada1' >>> self.that = 'yada2'
Displaying the Class Config¶
To output attributes of the myinst
instance of MyClass
, use the inherited show_config()
method.
Example with myinst as an instance of class MyClass:
>>> myinst = MyClass() >>> myinst.show_config() {that: yada2, this: yada1}
Loading Config from a File into Class Attributes¶
To load a config file into a dictionary attribute of a class instance, use the inherited load_config()
method.
Example loading a config from
examples/systems.yml
into class instancemyinst
:>>> myinst.load_config('examples/systems.yml') >>> myinst.show_config() clients: [192.168.1.225] masternode: 192.168.1.221 nodes: [192.168.1.221, 192.168.1.222, 192.168.1.223, 192.168.1.224] that: yada2 this: yada1
Storing Attributes of an Instance to File¶
To store the attributes of a class instance, use the inherited store_config()
method.
Example storing the attributes from the
myinst
instance ofMyClass
to file/tmp/myinst.yml
:>>> myinst.store_config('/tmp/myinst.yml')Looking at the contents of the resulting config file:
$ cat /tmp/myinst.yml clients: [192.168.1.225] masternode: 192.168.1.221 nodes: [192.168.1.221, 192.168.1.222, 192.168.1.223, 192.168.1.224] that: yada this: yada
Warning
Glusto will currently throw errors when using Instaconfig to store INI formatted config to file.
Currently, the best way to store in INI format would be to form your config data, and then use g.store_config()
.
Handling Logging with Glusto¶
Default Logging¶
By default, a logging object (glustolog
) is setup by Glusto.
It writes to /tmp/glusto.log
The glustolog
object is designed for use by Glusto itself.
If you need a log that is independent of the events logged by Glusto, you can
create a new log object for use in your scripts.
Setting up Logging¶
To create a new logging object, use the create_log
command:
>>> mylog = g.create_log(name='mylog', filename='/tmp/my.log')
The default severity level is INFO
.
To create a logging object with a severity level other than the default:
>>> mylog = g.create_log(name='mylog', filename='/tmp/my.log', level='WARNING')
The options are INFO
, WARNING
, ERROR
, CRITICAL
, and DEBUG
Sending a Log Event¶
To write a message to a log, use the standard Python logging methods with your log object.
An example sending a warning to
mylog
:>>> mylog.warning('this is a test to my log') >>> g.show_file('/tmp/my.log') 2016-07-10 09:07:11,591 WARNING (<module>) this is a test to my log
Available options are:
- <log>.debug
- <log>.info
- <log>.error
- <log>.warning
- <log>.critical
Sending Log Events to Multiple Logfiles¶
If you need to log to multiple files at the same time, you can add additional log handlers to an existing log object.
To add an additional logfile to an existing log object:
>>> g.add_log(g.mylog, filename='/tmp/my_other.log', level='CRITICAL')
If ‘STDOUT’ is passed as the filename, a log will be added that prints to stdout.
>>> g.add_log(g.mylog, filename='STDOUT') >>> g.mylog.info('This is a test log entry to stdout.') 2016-06-05 10:27:24,175 INFO (<module>) This is a test log entry to stdout.
Show the Logfiles Attached to a Specific Logger¶
To show a list of the logfiles attached to a logger, use the show_logs()
command.
>>> g.show_logs(g.mylog) Log: mylog - mylog1: /tmp/my.log (WARNING) - mylog2: /tmp/my_other.log (CRITICAL)
Removing a Log¶
If a logfile is no longer needed, remove the logfile from the logger with the remove_log()
command.
>>> g.show_logs(g.mylog) Log: mylog - mylog1: /tmp/my.log - mylog2: sys.stdout >>> g.remove_log(g.mylog, 'mylog1') >>> g.show_logs(g.mylog) Log: mylog - mylog1: /tmp/my.log
To remove all logfiles from a logger, use the remove_log
command without passing a name.
>>> g.remove_log(g.mylog)
Changing the Level of an Existing Log Handler¶
To change the level of an existing log, use the set_log_level()
method.
>>> g.show_logs(g.log) Log: glustolog - glustolog1: /tmp/glusto.log (DEBUG) - glustolog2: /tmp/testtrunc.log (INFO) >>> g.set_log_level('glustolog', 'glustolog2', 'WARNING') >>> g.show_logs(g.log) Log: glustolog - glustolog1: /tmp/glusto.log (DEBUG) - glustolog2: /tmp/testtrunc.log (WARNING)
Changing the Filename of an Existing Log Handler¶
To change the level of an existing log, use the set_log_filename()
method.
>>> g.show_logs(g.log) Log: glustolog - glustolog1: /tmp/glusto.log (DEBUG) - glustolog2: /tmp/testtrunc.log (INFO) >>> g.set_log_filename('glustolog', 'glustolog2', '/tmp/my.log') >>> g.show_logs(g.log) Log: glustolog - glustolog1: /tmp/glusto.log (DEBUG) - glustolog2: /tmp/my.log (WARNING)
Clearing a Log¶
To empty a logfile, use the clear_log()
method.
>>> g.show_logs(g.log) Log: glustolog - glustolog1: /tmp/glusto.log (DEBUG) - glustolog2: /tmp/testtrunc.log (INFO) >>> g.clear_log('glustolog', 'glustolog2')
Temporarily Disable Logging¶
There might be times when suspending logging at a certain level is necessary. For example, if a particular function tends to spam the log.
To suspend logging at a specific level, use the disable_log_levels()
method.
>>> g.disable_log_levels('WARNING')
Note
This will suspend logging for the specific level and all levels below it across all logs.
To resume logging at the previously defined levels, use the reset_log_levels()
method.
>>> g.reset_log_levels()
Logging with Color Text¶
With the simple ANSI color capability built into Glusto, it is possible to add color text in logs or other output.
Changing the Color of a String¶
To wrap a string in color, use the colorfy command.
>>> print g.colorfy(g.RED, 'This string is RED')
The printed string will be output in the color red and any following text will return to default color.
See the “Available Color Values” below for the full list of Foreground Colors.
Changing the Background Color of a String¶
It is possible to change the background color of a string.
>>> print g.colorfy(g.BG_YELLOW, 'This string has a YELLOW background')
See the “Available Color Values” below for the full list of Background Colors.
Changing an ANSI Attribute of a String¶
It is also possible to make a string bold.
>>> print g.colorfy(g.BOLD, 'This string is BOLD')
Warning
Mileage may vary depending on the output device.
See the “Available Color Values” below for the full list of Attributes.
Combining Colors and Attributes¶
Glusto allows multiple combinations of color and attributes to be used at the same time.
To combine colors and attributes, pass a Bitwise Or’d list to colorfy()
.
>>> print g.colorfy(g.BOLD | g. RED | g.BG_YELLOW, 'This string is BOLD and RED on a YELLOW BACKGROUND.')
Tip
Create your own combinations ahead of time for re-use throughout your script.
>>> COLOR_ALERT = g.BOLD | g.RED | g.REVERSE >>> COLOR_WARNING = g.BOLD | g.RED >>> print '%s %s' %(g.colorfy(COLOR_ALERT, 'WARNING:'), g.colorfy(COLOR_WARNING, 'This is a warning!'))
Send Color Text to the Log¶
Any of the previously discussed print commands can be replaced with logging commands to send the color text to logfiles.
>>> g.log.debug(g.colorfy(g.BOLD | g.RED | g.BG_YELLOW, 'This string is BOLD and RED on a YELLOW BACKGROUND.'))
Enabling Color Logging for Built-In Commands¶
Color output is enabled by default and some of the Glusto internal commands (e.g., g.run()
) already use color output for logging.
To disable the built-in color logging, add a line to the Glusto /etc/glusto/defaults.yml
file.
log_color: False
To enable the built-in color logging, add a line to the Glusto /etc/glusto/defaults.yml
file.
log_color: True
Color output can also be disabled by adding the log_color option to a config file loaded via /usr/bin/glusto -c
.
Another method is to set g.config['log_color'] = False
directly in your code.
Available Color Values¶
When using the color values listed in the table below, remember to add the Glusto g.
reference in front of each color value.
For example,g.BG_LTMAGENTA
BACKGROUND | FOREGROUND | ATTRIBUTES |
---|---|---|
BG_DEFAULT | DEFAULT | NORMAL |
BG_BLACK | BLACK | BOLD |
BG_RED | RED | DIM |
BG_GREEN | GREEN | UNDERLINE |
BG_YELLOW | YELLOW | BLINK |
BG_BLUE | BLUE | REVERSE |
BG_MAGENTA | MAGENTA | HIDDEN |
BG_CYAN | CYAN | |
BG_LTGRAY | LTGRAY | |
BG_DKGRAY | DKGRAY | |
BG_LTRED | LTRED | |
BG_LTGREEN | LTGREEN | |
BG_LTYELLOW | LTYELLOW | |
BG_LTBLUE | LTBLUE | |
BG_LTMAGENTA | LTMAGENTA | |
BG_LTCYAN | LTCYAN | |
BG_WHITE | WHITE |
Handling Templates with Glusto¶
Create a File from a Template¶
>>> g.render_template("templates/template_testcase.jinja", ... template_vars, "/tmp/testcase.py", searchpath='examples/')
The Template File¶
A Bit About Search Path¶
PyUnit (unittest) and Glusto¶
Glusto plugs into the Python unittest module methodology and can run test modules with the Glusto features imported or existing scripts without Glusto imported in the test module. This makes it possible to combine tests that do not require any of the Glusto functionality with Glusto-savvy test cases.
Running Unittests with Glusto¶
Glusto supports running test cases in a number of ways.
- Using the Python interpreter interactive mode to execute commands manually.
- Running an individual script at the command-line via
if __name__ == '__main__':
logic. - Running an individual script via an IDE (e.g., Eclipse PyDev plug-in [1] test runner).
- Running testcases directly at the command-line via the
python -m unittest
module. - Using the glusto cli command with CLI options or config files.
Each of the above options is documented below.
Note
All of the examples are based on the samples provided in the examples
directory installed with the glusto package.
Running Unittests via the Python Interpreter Interactive Mode¶
Running the tests via the Python interpreter can come in handy while developing and debugging tests scripts.
To run tests via the Python interpreter…
Enter the Python Interpreter.
# python
Type commands…
For example:
>>> import unittest >>> loader = unittest.TestLoader() >>> suite = loader.loadTestsFromName('tests.test_glusto.TestGlustoBasics.test_stdout') >>> runner = unittest.TextTestRunner() >>> runner.run(suite) Setting Up Class: TestGlustoBasics Setting Up: tests.test_glusto.TestGlustoBasics.test_stdout Running: tests.test_glusto.TestGlustoBasics.test_stdout - Testing output to stdout Tearing Down: tests.test_glusto.TestGlustoBasics.test_stdout Cleaning up after setup on fail or after teardown .Tearing Down Class: TestGlustoBasics ---------------------------------------------------------------------- Ran 1 test in 0.242s OK <unittest.runner.TextTestResult run=1 errors=0 failures=0>
Running Unittests with the CLI Option¶
The simplest and most direct way to run unittests with Glusto is via the glusto cli discover option.
The discover (-d,–discover) option accepts a directory name and the unittest
module discover feature [2] searches the directory and subdirectories for modules
with a name matching `test*.py`
.
Note
It is possible to fine-tune the discover options with a specific pattern and top_level_directory via the configuration option described below.
Example:
# glusto -d 'tests' -c 'examples/systems.yml'
Running Tests in a Specific Order¶
One of the most argued elements of unit testing is creating a relationship between test cases by specifying an order where one has to be run before the other to satisfy a dependency, etc. Some will say test cases should never be related and always standalone. Some say there is a need in integration testing where it makes sense to leverage one test case to setup another without jumping through programmatic hoops to prevent a dependency. Regardless of which camp you might side with, the Python unittest module can be leveraged for a variety of use cases, so Glusto provides a convenient interface to the capability added in Python 2.7.
Add the following example to the test module containing a standard unittest.TestCase class.
1 2 3 4 5 6 7 8 9 | def load_tests(loader, standard_tests, pattern):
'''Load tests in specified order'''
testcases_ordered = ['test_return_code',
'test_stdout',
'test_stderr']
suite = g.load_tests(TestGlustoBasics, loader, testcases_ordered)
return suite
|
See tests/test_glusto.py
for a full example of a unittest.TestCase using
Glusto and running tests in order.
Running Unittests with the Configuration Options¶
If more control over discovery options, or the ability to select tests at the module or test case level is required, you can use config files to specify those requirements.
To run tests with information from config files, use the -u option:
# glusto -u -c 'examples/systems.yml examples/unittests/unittest.yml examples/unittests/unittest_list.yml'
Configuring Glusto for Unittests¶
Along with the simple discovery method at the CLI, Glusto supports more granular control over Unittests via configuration files.
Base Unittest Options¶
Configuration items that control options Glusto-wide can be configured.
output_junit The
output_junit
option writes the test results in junit xml format.unittest: output_junit: falsetest_method_prefix The
test_method_prefix
option changes the name prefix used by unittest to discover tests.unittest: test_method_prefix: 'rhgs'
Discover Tests from a Directory¶
Discovery via config is similar to the CLI, but offers additional options.
Config:
# DISCOVER TESTS FROM DIRECTORY
discover_tests:
start_dir: 'tests'
# optional
pattern: 'test*.py'
top_level_dir: 'tests'
Load Tests from a List¶
To run a specific set of tests, Glusto supports configuring a list.
Config (unittest.yml):
# LOAD TESTS FROM LIST (SEE unittest_list.yml)
load_tests_from_list: true
Config (unittest_list.yml):
unittest_list:
module_name: 'tests.test_glusto'
list: [
'TestGlustoBasics.test_stdout',
'TestGlustoBasics.test_return_code',
'TestGlustoBasics.test_stderr',
'TestGlustoBasics.test_expected_fail',
]
Load Tests from a Module¶
To limit test list to only those in a specific module, use the load_tests_from_module
option.
Tests are discovered automatically and run in alphabetical order.
Config:
# LOAD TESTS FROM MODULE w/ TEST_LOAD ORDERED TESTS
load_tests_from_module:
module_name: 'tests.test_glusto'
use_load_tests: false
Load Tests from a Module with Ordered Test List¶
To limit the test list to a specific module and specify an order, set the use_load_test
option to true
.
Config:
# LOAD TESTS FROM MODULE w/o TEST_LOAD ORDERED TESTS
load_tests_from_module:
module_name: 'tests.test_glusto'
use_load_tests: true
Note
When setting use_load_tests: true
it is necessary to add a load_tests()
method to your test script.
For more information on the load_tests() method, please see the “Running Tests in a Specific Order” section earlier in this doc.
Load a Test Using a Name¶
To limit the test to a specific test module, class, or method, use the load_tests_from_name
option.
Config:
# LOAD TESTS FROM NAME
load_tests_from_name: 'tests.test_glusto.TestGlustoBasics.test_stdout'
When providing a module, the list is created from all tests in the module.
load_tests_from_name: 'tests.test_glusto'
When providing a class, the list is created from all tests in the class.
load_tests_from_name: 'tests.test_glusto_configs'
When providing a method, only that method is run.
load_tests_from_name: 'tests.test_glusto.TestGlustoBasics.test_stdout'
Load Tests from a List of Names¶
To limit the test to a list of names described above, use the load_tests_from_names
option.
Config:
# LOAD TESTS FROM LIST OF NAMES
load_tests_from_names: ['tests.test_glusto',
'tests.test_glusto_configs',
'tests.test_glusto.TestGlustoBasics.test_stdout']
The list will be composed of all tests combined.
Writing Unittests¶
Glusto’s unit test features are based on the Python unittest module. The unittest module provides a simple class structure that makes testcase development rather robust without modification.
To use the unittest
module for creating a testcase, import the unittest
module
and create a subclass of unittest.TestCase
.
1 2 3 | import unittest
class MyTestClass(unittest.TestCase)
|
The base class for unittest
is unittest.TestCase
. It consists of several
automatically called methods that are designed to be overridden to provide your own functionality.
Note
In the future, I will look at integrating some PyTest or other frameworks, but don’t have an immediate need.
Example Using setUp and tearDown¶
test_glusto_configs.py
Example Using setUpClass and tearDownClass¶
test_glusto_templates.py
To Do¶
- Expand the Writing Test Cases section with more examples.
Footnotes
[1] | http://www.pydev.org/ |
[2] | https://docs.python.org/2.7/library/unittest.html#unittest.TestLoader.discover |
PyTest and Glusto¶
Running PyTests from the CLI¶
Use the -t=
or --pytest=
parameter followed by the options normally passed to py.test
$ glusto -c 'examples/systems.yml' --pytest='-v -x tests -m response' Starting glusto via main() ... pytest: -v -x tests -m response ========================= test session starts ============================================ platform linux2 -- Python 2.7.11, pytest-2.9.2, py-1.4.31, pluggy-0.3.1 -- /usr/bin/python cachedir: .cache rootdir: glusto, inifile: collected 21 items tests/test_glusto_pytest.py::TestGlustoBasicsPyTest::test_return_code PASSED tests/test_glusto_pytest.py::TestGlustoBasicsPyTest::test_stderr PASSED tests/test_glusto_pytest.py::TestGlustoBasicsPyTest::test_stdout PASSED ========================= 18 tests deselected by "-m 'response'" ========================= ======================== 3 passed, 18 deselected in 0.62 seconds ========================= Ending glusto via main()
For a list of available options, pass --help
to the pytest
parameter or use the py.test
command itself.
$ glusto --pytest='--help' $ py.test --help
Running PyTest from Python Interactive Interpreter¶
>>> import pytest >>> pytest.main('-v -x tests -m response') ========================== test session starts =========================================== platform linux2 -- Python 2.7.11, pytest-2.9.2, py-1.4.31, pluggy-0.3.1 -- /usr/bin/python cachedir: .cache rootdir: glusto, inifile: collected 21 items tests/test_glusto_pytest.py::TestGlustoBasicsPyTest::test_return_code PASSED tests/test_glusto_pytest.py::TestGlustoBasicsPyTest::test_stderr PASSED tests/test_glusto_pytest.py::TestGlustoBasicsPyTest::test_stdout PASSED ========================= 18 tests deselected by "-m 'response'" ========================= ======================== 3 passed, 18 deselected in 0.55 seconds ========================= 0
To make config files available to test cases when running interactively,
use the load_config
and update_config
methods.
>>> from glusto.core import Glusto as g >>> config = g.load_config('examples/systems.yml') >>> g.update_config(config) >>> import pytest >>> pytest.main('-v -x tests -m response')
To Do¶
- Expand text and examples
Nose and Glusto¶
Running Nosetests from the CLI¶
Use the -n=
or --nostests=
parameter followed by the options normally passed to nosetests
$ glusto -c 'examples/systems.yml' --nosetests='-v -w tests' Starting glusto via main() ... nosetests: -v -w tests Testing an expected failure. This test should fail ... /usr/lib64/python2.7/unittest/case.py:378: RuntimeWarning: TestResult has no addExpectedFailure method, reporting as passes RuntimeWarning) ok Testing an expected failure as negative test ... ok Testing the return code ... ok Testing the unittest skip feature ... SKIP: Example test skip Testing output to stderr ... ok Testing output to stdout ... ok Load tests in a specific order. ... ERROR Testing ini config file(s) ... ok Testing ordered ini config file(s) ... ok Testing yaml config file ... ok Testing an expected failure. This test should fail ... FAIL Testing an expected failure as negative test ... ok Testing the return code ... ok Testing the unittest skip feature ... ok Testing output to stderr ... ok Testing output to stdout ... ok Load tests in a specific order. ... ERROR Testing rpyc connection ... ok Testing local module definition on remote system ... ok test_remote_call (tests.test_glusto_rpyc.TestGlustoRpyc) ... ok Testing template for loop ... ok Testing template include ... ok Testing template scalar ... ok ... ====================================================================== FAIL: Testing an expected failure. This test should fail ---------------------------------------------------------------------- Traceback (most recent call last): File "glusto/tests/test_glusto_pytest.py", line 98, in test_expected_fail self.assertEqual(rcode, 0) AssertionError: 1 != 0 -------------------- >> begin captured stdout << --------------------- Setting Up: tests.test_glusto_pytest.TestGlustoBasicsPyTest.test_expected_fail Running: tests.test_glusto_pytest.TestGlustoBasicsPyTest.test_expected_fail - Testing an expected failure. This test should fail --------------------- >> end captured stdout << ---------------------- -------------------- >> begin captured logging << -------------------- plumbum.local: DEBUG: Running ['/usr/bin/ssh', '-T', '-oPasswordAuthentication=no', '-oStrictHostKeyChecking=no', '-oPort=22', '-oConnectTimeout=10', '-oControlMaster=auto', '-oControlPersist=4h', '-oControlPath=~/.ssh/glusto-ssh-%r@%h:%p', 'root@192.168.1.221', 'cd', '/root', '&&', 'false'] --------------------- >> end captured logging << --------------------- ---------------------------------------------------------------------- Ran 23 tests in 2.791s FAILED (SKIP=1, errors=2, failures=1) Ending glusto via main()
For a list of available options, pass --help
to the nosetests
parameter or use the nosetests
command itself.
$ glusto --noseests='--help' $ nosetests --help
Running Nosetests from Python Interpreter¶
>>> import nose >>> nose.run(argv=['-v', '-w', 'tests'])
To Do¶
- Expand text and examples
Working with PyTest and Nose¶
It is easy to integrate Glusto capability into a PyTest or Nose formatted test script. Simply add the import to the script and all of the Glusto methods are available.
import pytest from glusto.core import Glusto as g
The Glusto command-line utility currently wraps the PyUnit mechanism for discovering and running tests. PyUnit tests using Glusto can also run under PyTest and, with some exceptions, Nose.
Note
Except where noted, the examples provided here are currently only written
with the Python unittest
module in mind. This section discusses running
those scripts under PyTest and Nose. At some point, I will write a set of
specific examples for each. For now, this discussion is intended to demonstrate
the flexibility Glusto as a library of utilities can provide across the
three popular test frameworks.
The following examples use the tests included in the tests
directory.
PyTest¶
With the provided examples, PyTest runs the tests without issue and even handles skipped tests and expected failures. There are some notable exceptions they make (e.g., ignoring test order with load_tests).
A simple py.test run:
$ py.test ==================== test session starts ============================= platform linux2 -- Python 2.7.11, pytest-2.9.2, py-1.4.31, pluggy-0.3.1 rootdir: /home/loadtheaccumulator/glusto, inifile: collected 21 items test_glusto.py x..s.. test_glusto_configs.py ... test_glusto_pytest.py x..s.. test_glusto_rpyc.py ... test_glusto_templates.py ... ============= 17 passed, 2 skipped, 2 xfailed in 2.63 seconds ========The same run with verbose output:
$ py.test -v ================== test session starts =============================== platform linux2 -- Python 2.7.11, pytest-2.9.2, py-1.4.31, pluggy-0.3.1 -- /usr/bin/python cachedir: ../.cache rootdir: /home/loadtheaccumulator/Dropbox/glusto, inifile: collected 21 items test_glusto.py::TestGlustoBasics::test_expected_fail <- ../../../../usr/lib64/python2.7/unittest/case.py xfail test_glusto.py::TestGlustoBasics::test_negative_test PASSED test_glusto.py::TestGlustoBasics::test_return_code PASSED test_glusto.py::TestGlustoBasics::test_skip_me <- ../../../../usr/lib64/python2.7/unittest/case.py SKIPPED test_glusto.py::TestGlustoBasics::test_stderr PASSED test_glusto.py::TestGlustoBasics::test_stdout PASSED test_glusto_configs.py::TestGlustoConfigs::test_ini PASSED test_glusto_configs.py::TestGlustoConfigs::test_ini_ordered PASSED test_glusto_configs.py::TestGlustoConfigs::test_yaml PASSED test_glusto_pytest.py::TestGlustoBasicsPyTest::test_expected_fail <- ../../../../usr/lib64/python2.7/unittest/case.py xfail test_glusto_pytest.py::TestGlustoBasicsPyTest::test_negative_test PASSED test_glusto_pytest.py::TestGlustoBasicsPyTest::test_return_code PASSED test_glusto_pytest.py::TestGlustoBasicsPyTest::test_skip_me SKIPPED test_glusto_pytest.py::TestGlustoBasicsPyTest::test_stderr PASSED test_glusto_pytest.py::TestGlustoBasicsPyTest::test_stdout PASSED test_glusto_rpyc.py::TestGlustoRpyc::test_connection PASSED test_glusto_rpyc.py::TestGlustoRpyc::test_local_module_on_remote PASSED test_glusto_rpyc.py::TestGlustoRpyc::test_remote_call PASSED test_glusto_templates.py::TestGlustoTemplates::test_template_forloop PASSED test_glusto_templates.py::TestGlustoTemplates::test_template_include PASSED test_glusto_templates.py::TestGlustoTemplates::test_template_scalar PASSED ============== 17 passed, 2 skipped, 2 xfailed in 2.85 seconds ========
PyTest supports running PyUnit and Nose tests, so it’s simple to leverage PyTest
features, such as markers, in a PyUnit script. See the test_glusto_pytest.py
script for an example of combining PyTest marker features in a unittest.
A test run of only tests with a py.test formatted marker response:
$ py.test -v -m "response" ===================== test session starts ============================ platform linux2 -- Python 2.7.11, pytest-2.9.2, py-1.4.31, pluggy-0.3.1 -- /usr/bin/python cachedir: ../.cache rootdir: /home/loadtheaccumulator/Dropbox/glusto, inifile: collected 21 items test_glusto_pytest.py::TestGlustoBasicsPyTest::test_return_code PASSED test_glusto_pytest.py::TestGlustoBasicsPyTest::test_stderr PASSED test_glusto_pytest.py::TestGlustoBasicsPyTest::test_stdout PASSED ============== 18 tests deselected by "-m 'response'" ================= ============== 3 passed, 18 deselected in 0.69 seconds ================
Nose¶
Nose successfully runs the test methods, but does not handle the load_tests function as easily as PyTest.
$ nosetests /usr/lib64/python2.7/unittest/case.py:378: RuntimeWarning: TestResult has no addExpectedFailure method, reporting as passes RuntimeWarning) ...S..E.........E...... ====================================================================== ERROR: Load tests in a specific order. ---------------------------------------------------------------------- Traceback (most recent call last): File "/usr/lib/python2.7/site-packages/nose/case.py", line 197, in runTest self.test(*self.arg) TypeError: load_tests() takes exactly 3 arguments (0 given) ====================================================================== ERROR: Load tests in a specific order. ---------------------------------------------------------------------- Traceback (most recent call last): File "/usr/lib/python2.7/site-packages/nose/case.py", line 197, in runTest self.test(*self.arg) TypeError: load_tests() takes exactly 3 arguments (0 given) ---------------------------------------------------------------------- Ran 23 tests in 2.253s FAILED (SKIP=1, errors=2)
Twenty-three tests run successfully, two with errors (expected though), and 1 skipped. Apparently Nose ignores the py.test markers and did not skip a test in the py.test example.
A Nose test run with results written to an xunit xml file:
$ nosetests --with-xunit --xunit-file=/tmp/nosetests.xml
Glusto for Good Measure¶
$ glusto -d 'tests' Starting glusto via main() ... ---------------------------------------------------------------------- Ran 21 tests in 2.522s OK (skipped=1, expected failures=2)
Not surprisingly, the unittest
module does not recognize the PyTest skip marker,
so it is currently necessary to run PyTest-savvy scripts with the py.test
command.
To Do¶
- split this out and merge into individual test framework pages
- Add examples of PyTest and Nose specific test scripts using Glusto calls.
more on this subject later…
Dynamic Cartesian Product Test Case Creation¶
Glusto provides a decorator class that, given some variables by overriding the __init__ method and passing data in a decorator, will create test cases on the fly based on cartesian product combinations.
Currently, the only decorator available in Carteplex is the CarteTestClass.
It was written with unittest.TestCase
in mind and includes some wiring
specific to unittest (leveraging load_tests, test class-based, etc.).
The Cartetestclass is available with the same Glusto import that provides the
other functionality.
Note
Carteplex is a new feature to meet a very specific use case. Currently, the unittest and PyTest loaders/runners work without issue. The Nose runner currently chokes on the load_test function. The goal, of course, is to keep it generic and make it work across the test frameworks. Working on it.
Using Carteplex¶
To make the CarteTestClass decorator available to your test class, import Glusto.
from glusto.core import Glusto as g
An Example Config File¶
Test case scripts accept dynamic configuration variables from config files. In this case, a configuration state is added to a config file for the base class to use when creating the test case classes on the fly.
1 2 | run_on_volumes : 'ALL'
run_on_mounts : 'ALL'
|
Subclassing the CarteTestClass Decorator Class¶
Providing information specific to the tests being run is necessary for the
decorator to work correctly. This is accomplished by subclassing the
decorator class and overriding the __init__()
method to provide the
specific data.
There are three attributes in particular that the CarteTestClass uses to create the resulting array of test cases.
- axis_names
- This is a list of names used to automatically create class attributes in
the resulting test classes. For example, adding the name volume results
in the class attribute named
volume
being added to the test class with the value resulting from the cartesian product process. In this way, the test methods have access to the values of that specific combination. - selections
- This is a list of lists containing the options passed in via config file. For example, in the case of the gluster examples below, the desired values for the configurations to be run are passed in via a config file. The decorator in this case would be used to limit the configurations each test class can be run on. The CarteTestClass intersects the selections lists with the limits list to create the resulting list of used to create a test class for each combination.
- limits
- Limits are automatically passed in via the decorator and are used to limit the selections the test case can/will run on.
- available_options
- This is the full list of all values allowed for a specific attribute and is used to populate the selections value when ‘ALL’ is specified for the selections attribute.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | import unittest
from glusto.core import Glusto as g
class runs_on(g.CarteTestClass):
"""Decorator providing runs_on capability for standard unittest script"""
def __init__(self, value):
self.axis_names = ['volume', 'mount']
self.available_options = [['distributed', 'replicated',
'distributed-replicated',
'disperse', 'distributed-disperse'],
['glusterfs', 'nfs', 'cifs']]
config = g.load_config('gluster_config.yml')
if config:
g.update_config(config)
run_on_volumes = g.config.get('run_on_volumes',
self.available_options[0])
run_on_mounts = g.config.get('run_on_mounts',
self.available_options[1])
self.selections = [run_on_volumes, run_on_mounts]
self.limits = value
|
Creating a Custom unittest.TestCase Subclass¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | class GlusterBaseClass(unittest.TestCase):
@classmethod
def setUpClass(cls):
print "setUpClass: %s" % cls.__name__
print "SETUP GLUSTER VOLUME: %s on %s" % (cls.volume, cls.mount)
def setUp(self):
"""Setting this up"""
print "\tsetUp: %s - %s" % (self.id(), self.shortDescription())
def tearDown(self):
print "\ttearDown: %s - %s" % (self.id(), self.shortDescription())
@classmethod
def tearDownClass(cls):
print "tearDownClass: %s" % cls.__name__
print "TEARDOWN GLUSTER VOLUME: %s on %s" % (cls.volume, cls.mount)
|
Using the Decorator¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | from glusto.core import Glusto as g
from gluster_base_class import GlusterBaseClass
from gluster_base_class import runs_on
import pytest
import unittest
volumes = ['distributed', 'replicated', 'disperse']
mounts = ['glusterfs', 'nfs']
@runs_on([volumes, mounts])
class MyGlusterTest(GlusterBaseClass):
def test_gluster1(self):
"""Test 1"""
print "\t\tRunning: %s - %s" % (self.id(), self.shortDescription())
print "\t\t%s on mount %s" % (self.volume, self.mount)
@pytest.mark.test2
def test_gluster2(self):
"""Test 2"""
print "\t\tRunning: %s - %s" % (self.id(), self.shortDescription())
print "\t\t%s on mount %s" % (self.volume, self.mount)
@pytest.mark.skip
def test_gluster3(self):
"""Test 3"""
print "\t\tRunning: %s - %s" % (self.id(), self.shortDescription())
print "\t\t%s on mount %s" % (self.volume, self.mount)
|
Run the Tests¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | $ glusto -c 'examples/systems.yml tests_gluster/gluster_conf.yml' --pytest='-vv -q tests_gluster/test_gluster_runsauto.py'
Starting glusto via main()
...
pytest: -vvv -q tests_gluster/test_gluster_runsauto.py
==================================================================================== test session starts =====================================================================================
platform linux2 -- Python 2.7.11, pytest-2.9.2, py-1.4.31, pluggy-0.3.1 -- /usr/bin/python
cachedir: .cache
rootdir: glusto, inifile:
collected 18 items
tests_gluster/test_gluster_runsauto.py::MyGlusterTest_distributed_nfs::test_gluster1 PASSED
tests_gluster/test_gluster_runsauto.py::MyGlusterTest_distributed_nfs::test_gluster2 PASSED
tests_gluster/test_gluster_runsauto.py::MyGlusterTest_distributed_nfs::test_gluster3 SKIPPED
tests_gluster/test_gluster_runsauto.py::MyGlusterTest_replicated_glusterfs::test_gluster1 PASSED
tests_gluster/test_gluster_runsauto.py::MyGlusterTest_replicated_glusterfs::test_gluster2 PASSED
tests_gluster/test_gluster_runsauto.py::MyGlusterTest_replicated_glusterfs::test_gluster3 SKIPPED
tests_gluster/test_gluster_runsauto.py::MyGlusterTest_disperse_glusterfs::test_gluster1 PASSED
tests_gluster/test_gluster_runsauto.py::MyGlusterTest_disperse_glusterfs::test_gluster2 PASSED
tests_gluster/test_gluster_runsauto.py::MyGlusterTest_disperse_glusterfs::test_gluster3 SKIPPED
tests_gluster/test_gluster_runsauto.py::MyGlusterTest_disperse_nfs::test_gluster1 PASSED
tests_gluster/test_gluster_runsauto.py::MyGlusterTest_disperse_nfs::test_gluster2 PASSED
tests_gluster/test_gluster_runsauto.py::MyGlusterTest_disperse_nfs::test_gluster3 SKIPPED
tests_gluster/test_gluster_runsauto.py::MyGlusterTest_distributed_glusterfs::test_gluster1 PASSED
tests_gluster/test_gluster_runsauto.py::MyGlusterTest_distributed_glusterfs::test_gluster2 PASSED
tests_gluster/test_gluster_runsauto.py::MyGlusterTest_distributed_glusterfs::test_gluster3 SKIPPED
tests_gluster/test_gluster_runsauto.py::MyGlusterTest_replicated_nfs::test_gluster1 PASSED
tests_gluster/test_gluster_runsauto.py::MyGlusterTest_replicated_nfs::test_gluster2 PASSED
tests_gluster/test_gluster_runsauto.py::MyGlusterTest_replicated_nfs::test_gluster3 SKIPPED
============================================================================ 12 passed, 6 skipped in 0.04 seconds ============================================================================
Ending glusto via main()
|
ToDo¶
- Add decorator for function and method.
- Finish this document.
Glusto Simple REST Client¶
Glusto provides simple methods for basic REST API get, post, put, and delete functionality.
Making REST API Requests¶
Glusto supports the four basic REST API request types.
- GET
- POST
- PUT
- DELETE
Making a GET Request¶
To submit a GET request to a url, use the rest_get()
method.
>>> g.rest_get('http://httpbin.org/get')
Making a POST Request¶
To submit a POST request to a url, use the rest_post()
method.
>>> g.rest_post('http://httpbin.org/post', data={'this': 'yada1', 'that': 'yada2'})
Making a PUT Request¶
To submit a PUT request to a url, use the rest_put()
method.
>>> g.rest_put('http://httpbin.org/put', data={'this': 'yada1', 'that': 'yada2'})
Making a DELETE Request¶
To submit a DELETE request to a url, use the rest_delete()
method.
>>> g.rest_delete('http://httpbin.org/delete', data={'this': 'yada1', 'that': 'yada2'})
Handling the Request Response¶
Glusto provides the return in a tuple similar to the SSH calls in the glusto.Connectible
class.
>>> g.rest_get('http://192.168.1.112:8081/hello') (0, 'HelloWorld from GlusterFS Application', None)
The returned tuple consists of the return code, the response output, and the response error.
Note
The return code is the standard HTTP code returned by the web server on server response error (e.g., 404), otherwise returns zero for a successful request.
Using the Request Response as Config¶
The output of the response will be in string format.
If the string is yaml formatted text, it can be converted into a dictionary object
using the load_yaml_string()
method.
>>> g.rest_get('http://192.168.1.112:8081/clusters') (0, '{"clusters":["e2effa75a5a50560c3250b67cf71b465"]}\n', None) >>> rcode, rout, rerr = g.rest_get('http://192.168.1.112:8081/clusters')[1] >>> g.load_yaml_string(rout) {'clusters': ['e2effa75a5a50560c3250b67cf71b465']}
If the string is json formatted text, it can be converted into a dictionary object
using the load_json_string()
method.
>>> g.rest_get('http://192.168.1.112:8081/clusters') (0, '{"clusters":["e2effa75a5a50560c3250b67cf71b465"]}\n', None) >>> rcode, rout, rerr = g.rest_get('http://192.168.1.112:8081/clusters')[1] >>> g.load_json_string(out) {u'clusters': [u'e2effa75a5a50560c3250b67cf71b465']}
To Do¶
- better docs and more examples
- move print noise to log info and debug
API¶
glusto package¶
Submodules¶
glusto.carteplex module¶
Cartesian product decorator class for on-the-fly creation of testcases based on a matrix of lists.
- Handles automatic generation of cartesian product combinations based on any
- number of lists.
- Provides an ‘ALL’ default to populate a var with a predefined full list.
- Compares variables passed in decorator to a list of configuration options
- (e.g., specify what underlying configuration is available to the test via config file and specify the configurations the test is limited to… Glusto will only create the possible combinations based on the intersection).
- Passes “axis names” into test class as class attributes to make them
- available to test methods.
- Provided via the Glusto import “from glusto.core import Glusto as g”
To use, 1) Subclass CarteTestClass, 2) Override __init__ to define variables, 3) Import your subclass and add a decorator of the same name to your tests.
Carteplex is “Cartesian Multiplexing”. And that’s simplified from the original Carteprodplexorator (Cartesian Product Multiplexing Decorator).
See docs for more information and use-case examples.
-
class
glusto.carteplex.
Carteplex
¶ Bases:
object
Main class for cartesian product classes
-
class
CarteTestClass
(value)¶ Bases:
object
Decorator providing cartesian product parameter-like capability for unittest class
Override to provide data specific to your tests.
Parameters: value (object) – data automatically provided by the decorator. -
__call__
(obj)¶ The engine behind the cartesian product multiplexing (carteplex) goodness. Do not override.
Parameters: obj (object) – object automatically passed by the decorator. Returns: An empty object. (removes original testclass from run)
-
-
class
glusto.colorfiable module¶
All things ANSI color text output.
Note
Colorfiable is inherited by the Glusto class and not designed to be instantiated.
-
class
glusto.colorfiable.
Colorfiable
¶ Bases:
object
Defines and displays ANSI-compatible colors for string formatting.
-
BG_DEFAULT
= 1¶
-
BG_BLACK
= 2¶
-
BG_RED
= 4¶
-
BG_GREEN
= 8¶
-
BG_YELLOW
= 16¶
-
BG_BLUE
= 32¶
-
BG_MAGENTA
= 64¶
-
BG_CYAN
= 128¶
-
BG_LTGRAY
= 256¶
-
BG_DKGRAY
= 512¶
-
BG_LTRED
= 1024¶
-
BG_LTGREEN
= 2048¶
-
BG_LTYELLOW
= 4096¶
-
BG_LTBLUE
= 8192¶
-
BG_LTMAGENTA
= 16384¶
-
BG_LTCYAN
= 32768¶
-
BG_WHITE
= 65536¶
-
DEFAULT
= 131072¶
-
BLACK
= 262144¶
-
RED
= 524288¶
-
GREEN
= 1048576¶
-
YELLOW
= 2097152¶
-
BLUE
= 4194304¶
-
MAGENTA
= 8388608¶
-
CYAN
= 16777216¶
-
LTGRAY
= 33554432¶
-
DKGRAY
= 67108864¶
-
LTRED
= 134217728¶
-
LTGREEN
= 268435456¶
-
LTYELLOW
= 536870912¶
-
LTBLUE
= 1073741824¶
-
LTMAGENTA
= 2147483648¶
-
LTCYAN
= 4294967296¶
-
WHITE
= 8589934592¶
-
NORMAL
= 0¶
-
BOLD
= 17179869184¶
-
DIM
= 34359738368¶
-
UNDERLINE
= 68719476736¶
-
BLINK
= 137438953472¶
-
REVERSE
= 274877906944¶
-
HIDDEN
= 549755813888¶
-
COLOR_COMMAND
= 17246978048¶ Constant for command strings (BOLD | DKGRAY)
-
COLOR_STDOUT
= 17180131584¶ Constant for stdout (BOLD | BG_LTGRAY | BLACK)
-
COLOR_STDERR
= 17180393472¶ Constant for stderr (BOLD | RED)
-
COLOR_RCODE
= 17184063488¶ Constant for command return code (BOLD | BLUE)
-
classmethod
colorfy
(color, message)¶ Applies ANSI terminal colors and attributes to strings.
Parameters: - color (int) – Bitwise value(s) for color settings.
- message (str) – String to wrap in the specified color.
Returns: A color formatted string.
Example
>>> g.colorfy(g.BG_CYAN | g.RED | g.BOLD, 'Bold red text on cyan')
-
glusto.configurable module¶
All things configuration.
Note
Configurable is inherited by the Glusto class and not designed to be instantiated.
-
class
glusto.configurable.
Configurable
¶ Bases:
object
The class providing all things configuration.
-
config
= {}¶ The default class attribute for storing configurations.
-
static
store_config
(obj, filename, config_type=None, **kwargs)¶ - Writes an object to a file format.
- Automatically detects format based on filename extension.
Parameters: - obj (object) – The Python object to store in file.
- filename (str) – Filename for output of configuration.
- config_type (optional[str]) – The type of config file. Use when extension needs to differ from actual type. (e.g., .conf instead of .yml)
Returns: Nothing
Note
Uses custom GDumper class to strip Python object formatting. This is not a utility function for serialization.
-
static
load_config
(filename, config_type=None, **kwargs)¶ Reads a config from file. Defaults to yaml, but will detect other config formats based on filename extension. Currently reads yaml, json, ini, csv, and text files.
Parameters: - filename (str) – Filename of configuration to be read.
- config_type (optional[str]) – The type of config file. Use when extension needs to differ from actual type. (e.g., .conf instead of .yml)
- **kwargs (optional[dict]) – keyword arguments specific to formats
Returns: Dict of configuration items.
-
static
load_yaml_string
(yaml_string)¶ Reads a yaml formatted string into a dictionary
Parameters: yaml_string (str) – A string containing yaml formatted text. Returns: Dictionary on success.
-
static
load_json_string
(json_string)¶ Reads a json formatted string into a dictionary
Parameters: json_string (str) – A string containing json formatted text. Returns: Dictionary on success.
-
static
load_configs
(filelist)¶ Reads multiple configs from a list of filenames into a single configuration.
Parameters: filelist (list) – List of configuration filenames to read. Returns: Dict of configuration items.
-
classmethod
load_config_defaults
()¶
-
classmethod
set_config
(config)¶ Assigns a config to the config class attribute.
Parameters: config (dict) – A dictionary of configuration objects. Returns: Nothing Warning
DESTRUCTIVE. This will assign a new dictionary on top of an existing config. See update_config().
-
classmethod
update_config
(config)¶ Adds a config to the config class attribute.
Parameters: config (dict) – A dictionary of configuration objects. Returns: Nothing Warning
SOMEWHAT DESTRUCTIVE. This will overwrite any previously existing objects.
For example, config[‘thisandthat’] will overwrite cls.config[‘thisandthat’], but config[‘subconfig’][‘thisandthat’] will add the subconfig dictionary without overwriting cls.config[‘thisandthat’].
-
classmethod
log_config
(obj)¶ Writes a yaml formatted configuration to the log.
Parameters: obj (dict) – The configuration object to write to log. Returns: Nothing
-
static
get_config
(obj)¶ Retrieves an object in yaml format.
Parameters: obj (object) – A Python object to be converted to yaml. Returns: A yaml formatted string.
-
static
show_config
(obj)¶ Outputs a yaml formatted representation of an object on stdout.
Parameters: obj (object) – A Python object to be converted to yaml. Returns: Nothing
-
classmethod
clear_config
()¶ Clears the config class attribute with an empty dictionary.
Returns: Nothing
-
static
show_file
(filename)¶ Reads a file and prints the output.
Parameters: filename (str) – Name of the file to display. Returns: Nothing
-
-
class
glusto.configurable.
GDumper
(stream, default_style=None, default_flow_style=None, canonical=None, indent=None, width=None, allow_unicode=None, line_break=None, encoding=None, explicit_start=None, explicit_end=None, version=None, tags=None)¶ Bases:
yaml.dumper.Dumper
Override the alias junk normally output by Dumper. This is necessary because PyYaml doesn’t give a simple option to modify the output and ignore tags, aliases, etc.
-
ignore_aliases
(data)¶ Overriding to skip aliases.
-
prepare_tag
(tag)¶ Overriding to skip tags. e.g., !!python/object:glusto.cluster.Cluster
-
-
class
glusto.configurable.
Intraconfig
¶ Bases:
object
Class to provide instances with simple configuration utility and introspection in yaml config format.
Intended to be inherited.
Example
To inherit Intraconfig in your custom class:
>>> from glusto.configurable import Intraconfig >>> class MyClass(Intraconfig): >>> def __init__(self): >>> self.myattribute = "this and that"
To use Intraconfig to output MyClass as yaml:
>>> myinstance = MyClass() >>> myinstance.show_config()
-
update_config
(config)¶ Adds a config to the config class attribute.
Parameters: config (dict) – A dictionary of configuration objects. Returns: Nothing Warning
SOMEWHAT DESTRUCTIVE. This will overwrite any previously existing objects.
For example, config[‘thisandthat’] will overwrite cls.config[‘thisandthat’], but config[‘subconfig’][‘thisandthat’] will add the subconfig dictionary without overwriting cls.config[‘thisandthat’].
-
show_config
()¶ Outputs a yaml formatted representation of an instance on stdout.
Returns: Nothing
-
get_config
()¶ Retrieves an instance object in yaml format.
Returns: A yaml formatted string.
-
load_config
(filename)¶ Reads a yaml config from file and assigns to the config instance attribute.
Parameters: filename (str) – Filename of configuration to be read. Returns: Nothing
-
store_config
(filename, config_type=None)¶ - Writes attributes of a class instance to a file in a config format.
- Automatically detects format based on filename extension.
Parameters: - filename (str) – Filename for output of configuration.
- config_type (optional[str]) – The type of config file. Use when extension needs to differ from actual type. (e.g., .conf instead of .yml)
Returns: Nothing
Note
Uses custom GDumper class to strip Python object formatting. This is not a utility function for serialization.
-
glusto.connectible module¶
All things remote connection and local shell.
Note
Connectible is inherited by the Glusto class and not designed to be instantiated.
-
class
glusto.connectible.
Connectible
¶ Bases:
object
The class provding remote connections and local commands.
-
use_controlpersist
= True¶
-
user
= 'root'¶
-
classmethod
run
(host, command, user=None, log_level=None)¶ Run a command on a remote host via ssh.
Parameters: - host (str) – The hostname of the system.
- command (str) – The command to run on the system.
- user (optional[str]) – The user to use for connection.
- log_level (optional[str]) – only log stdout/stderr at this level.
Returns: A tuple consisting of the command return code, stdout, and stderr. None on error.
Example
To run the uname command on a remote host named “bunkerhill”…
>>> from glusto.core import Glusto as g >>> results = g.run("bunkerhill", "uname -a")
-
classmethod
run_async
(host, command, user=None, log_level=None)¶ Run remote commands asynchronously.
Parameters: - host (str) – The hostname of the system.
- command (str) – The command to run on the system.
- user (optional[str]) – The user to use for connection.
- log_level (optional[str]) – only log stdout/stderr at this level.
Returns: An open connection descriptor to be used by the calling function. None on error.
Example
To run a command asynchronously on remote hosts named “bunkerhill” and “breedshill”…
>>> from glusto.core import Glusto as g
>>> command = "ls -R /etc" >>> proc1 = g.run_async("bunkerhill", command) >>> proc2 = g.run_async("breedshill", command)
>>> results1 = proc1.async_communicate() >>> results2 = proc2.async_communicate()
This can also be used to run a command against the same system asynchronously as different users…
>>> command = "ls -R /etc" >>> proc1 = g.run_async("breedshill", command, user="howe") >>> proc2 = g.run_async("breedshill", command, user="pigot")
>>> results1 = proc1.async_communicate() >>> results2 = proc2.async_communicate()
Note
run_async() runs commands asynchronously, but blocks on async_communicate() and reads output sequentially. This might not be a good fit for run-and-forget commands.
-
classmethod
run_local
(command, log_level=None)¶ Run a command on the local management system.
Parameters: - command (str) – Command to run locally.
- log_level (optional[str]) – only log stdout/stderr at this level.
Returns: A tuple consisting of the command return code, stdout, and stderr.
Example
To run a command locally…
>>> from glusto.core import Glusto as g >>> retcode, stdout, stderr = g.run_local("uname -a")
-
classmethod
run_serial
(hosts, command, user=None, log_level=None)¶ Sequentially runs a command against a list of hosts.
Parameters: - hosts (list) – A list of hostnames to run command against.
- command (str) – The command to run on the system.
- user (optional[str]) – The user to use for connection.
- log_level (optional[str]) – only log stdout/stderr at this level.
Returns: A dictionary of tuples containing returncode, stdout, and stderr. Labeled by the host.
Example
To run a command against a list of hosts…
>>> from glusto.core import Glusto as g >>> hosts = ["bunkerhill", "breedshill"] >>> results = g.run_serial(hosts, "ls -Rail /etc")
-
classmethod
run_parallel
(hosts, command, user=None, log_level=None)¶ Runs a command against a list of hosts in parallel.
Parameters: - hosts (list) – A list of hostnames to run command against.
- command (str) – The command to run on the system.
- user (optional[str]) – The user to use for connection.
- log_level (optional[str]) – only log stdout/stderr at this level.
Returns: A dictionary of tuples containing returncode, stdout, and stderr. Labeled by the host.
Example
To run a command against a list of hosts in parallel…
>>> from glusto.core import Glusto as g >>> hosts = ["bunkerhill", "breedshill"] >>> results = g.run_serial(hosts, "ls -Rail /etc")
-
classmethod
upload
(host, localpath, remotepath, user=None)¶ Uploads a file to a remote system.
Parameters: - host (str) – Hostname of the remote system.
- localpath (str) – The source path for the file on the local system.
- remotepath (str) – The target path on the remote server.
- user (optional[str]) – The user to use for the remote connection.
Returns: None on failure.
-
classmethod
download
(host, remotepath, localpath, user=None)¶ Downloads a file from a remote system.
Parameters: - host (str) – Hostname of the remote system.
- remotepath (str) – The source path on the remote server.
- localpath (str) – The target path for the file on the local system.
- user (optional[str]) – The user to use for the remote connection.
Returns: None on failure.
-
classmethod
transfer
(sourcehost, sourcefile, targethost, targetfile, user=None)¶ Transfer a file between remote systems (scp) Requires keys to be set up between remote systems.
Parameters: - sourcehost (str) – Hostname of the remote system copying from.
- sourcefile (str) – The source path on a remote system.
- targethost (str) – Hostname of the remote system copying to.
- targetfile (str) – The target path for the file on a remote system.
- user (optional[str]) – The user to use for the remote connection.
Returns: Nothing
-
classmethod
ssh_list_connections
()¶ Display the list of existing ssh connections on stdout.
-
classmethod
ssh_get_connections
()¶ Retrieves the dictionary of ssh connections.
Returns: A dictionary of ssh connections.
-
classmethod
ssh_close_connection
(host, user=None)¶ Close an SshMachine connection.
Parameters: - host (str) – Hostname of the system.
- user (optional[str]) – User to use for connection.
Returns: Nothing
-
classmethod
ssh_close_connections
()¶ Close all ssh connections.
Parameters: None – Returns: Nothing
-
classmethod
ssh_set_keyfile
(keyfile)¶
-
classmethod
ssh_get_keyfile
()¶
-
glusto.core module¶
The brains of the Glusto toolset.
Glusto inherits from multiple classes providing configuration, remote connection, and logging functionality and presents them in a single global Class object. Glusto also acts a global class for maintaining state across multiple modules and classes.
Example
To use Glusto in a module, import at the top of each module leveraging the glusto tools.:
from glusto.core import Glusto as g
-
class
glusto.core.
Glusto
¶ Bases:
glusto.configurable.Configurable
,glusto.connectible.Connectible
,glusto.colorfiable.Colorfiable
,glusto.loggable.Loggable
,glusto.templatable.Templatable
,glusto.unittestable.Unittestable
,glusto.restable.Restable
,glusto.rpycable.Rpycable
,glusto.carteplex.Carteplex
The locker for all things Glusto.
-
log
= <logging.Logger object>¶
-
glusto.loggable module¶
All things logging.
Note
Loggable is inherited by the Glusto class and not designed to be instantiated.
-
class
glusto.loggable.
Loggable
¶ Bases:
object
The class providing logging functionality.
-
handler_counter
= 1¶
-
classmethod
create_log
(name='glustolog', filename='/tmp/glusto.log', level='DEBUG', log_format=None, allow_multiple=False)¶ Creates a log object using the Python “logging” module.
Parameters: - name (optional[str]) – The reference name for the logging object. Defaults to “glustolog”.
- filename (optional[str]) – Fully-qualified path and filename. Defaults to “/tmp/glusto.log”.
- level (optional[str]) – The minimum log level. Defaults to “INFO”.
- allow_multiple (bool) – Can multiple logfiles exist? Tells method whether to create a new handler or wipe existing before creating a new handler.
Returns: A logging object.
-
classmethod
add_log
(logobj, filename='/tmp/glusto.log', level='INFO', log_format=None)¶ Add a logfile to the logobj
Parameters: - logobj (object) – A logging object.
- filename (optional[str]) – Fully-qualified path and filename. Defaults to “/tmp/glusto.log”.
- level (optional[str]) – The minimum log level. Defaults to “INFO”.
-
classmethod
remove_log
(logobj, name=None)¶ Remove a log handler from a logger object.
Parameters: - logobj (object) – A logging object.
- name (optional[str]) – The name of the log handler to remove. If None, will remove all log handlers from the logger.
-
classmethod
show_logs
(logobj)¶ Show a list of log handlers attached to a logging object
Parameters: logobj (object) – A logging object.
-
classmethod
disable_log_levels
(level)¶ Disable level (and lower) across all logs and handlers. Handy if a method continually spams the logs. Use reset_log_level() to return to normal logging.
Note
See Python logging module docs for more information.
Parameters: level (str) – String name for the top log level to disable. Returns: Nothing
-
classmethod
reset_log_levels
()¶ Reset logs to current handler levels. Convenience method to undo disable_log_level()
Parameters: None – Returns: Nothing
-
classmethod
set_log_level
(log_name, handler_name, level)¶ Set the log level for a specific handler. Use show_logs() to get the list of log and handler names.
Parameters: - log_name (str) – The name of the log.
- handler_name (str) – The name of the specific log handler.
- level (str) – The string representation of the log level.
Returns: Nothing
-
classmethod
set_log_filename
(log_name, handler_name, filename)¶ Change the logfile name for a specific handler. Use show_logs() to get the list of log and handler names.
Parameters: - log_name (str) – The name of the log.
- handler_name (str) – The name of the specific log handler.
- filename (str) – The path/filename to log to.
Returns: Nothing
Note
Nothing in logging docs mentions this method (close and set baseFilename) over removing the handler and creating a new handler with the new filename. Research and correct if needed. Caveat emptor.
-
classmethod
clear_log
(log_name, handler_name)¶ Empties an existing log file
Parameters: - log_name (str) – The name of the log.
- handler_name (str) – The name of the specific log handler.
Returns: Nothing
-
glusto.main module¶
Glusto CLI wrapper
-
glusto.main.
handle_configs
(config_list)¶ Load default and user-specified configuration files
-
glusto.main.
main
()¶ Entry point console script for setuptools.
Provides a command-line interface to Glusto.
Currently does nothing useful, but plan to wrap Glusto functionality in a CLI interface that can be injected into shell scripts, etc.
Example
# glusto run hostname.example.com “uname -a”
glusto.restable module¶
All things REST API related.
Note
Restable is inherited by the Glusto class and not designed to be instantiated.
-
class
glusto.restable.
Restable
¶ Bases:
object
The class providing REST API functionality.
-
classmethod
rest_get
(url)¶ Submit a REST API GET request.
Parameters: url (str) – The HTTP protocol standard url for the request. Returns: The HTTP protocol standard return code. Zero on success. response output (str): The output text from the response. response error (str): The error text on failure. Return type: returncode (int)
-
classmethod
rest_post
(url, data)¶ Submit a REST API POST request.
Parameters: - url (str) – The HTTP protocol standard url for the request.
- data (dict) – A dictionary of key:value pairs
Returns: The HTTP protocol standard return code. Zero on success. response output (str): The output text from the response. response error (str): The error text on failure.
Return type: returncode (int)
-
classmethod
rest_put
(url, data)¶ Submit a REST API PUT request.
Parameters: - url (str) – The HTTP protocol standard url for the request.
- data (dict) – A dictionary of key:value pairs
Returns: The HTTP protocol standard return code. Zero on success. response output (str): The output text from the response. response error (str): The error text on failure.
Return type: returncode (int)
-
classmethod
rest_delete
(url, data)¶ Submit a REST API DELETE request.
Parameters: - url (str) – The HTTP protocol standard url for the request.
- data (dict) – A dictionary of key:value pairs
Returns: The HTTP protocol standard return code. Zero on success. response output (str): The output text from the response. response error (str): The error text on failure.
Return type: returncode (int)
-
classmethod
glusto.rpycable module¶
All things rpyc connection.
Note
Rpycable is inherited by the Glusto class and not designed to be instantiated.
Warning
Rpyc breaks in mixed Python 2.x/3.x environments. When using rpyc, you will only be able to successfully make rpyc calls against a system running the same version of Python. (see rpyc module install docs for more information)
-
class
glusto.rpycable.
Rpycable
¶ Bases:
object
-
classmethod
rpyc_get_connection
(host, user=None, instance=1)¶ Setup and cache a connection via rpyc.
Parameters: - host (str) – The hostname or IP of the remote system.
- user (str) – A user on the remote system. Default: root
- instance (int) – The number of the instance when multiple connections are used.
Returns: A new or cached rpyc connection object.
-
classmethod
rpyc_create_connections
(hosts, user=None, num_instances=1)¶ Setup and cache multiple connections via rpyc.
Parameters: - host (str) – The hostname or IP of the remote system.
- user (str) – A user on the remote system. Default: root
- num_instances (int) – The number of the instances to create.
Returns: Nothing.
-
classmethod
rpyc_get_connections
()¶ Get the connection dictionary.
Parameters: None – Returns: The dictionary of rpyc connections.
-
classmethod
rpyc_list_connections
()¶ Display the list of existing ssh connections on stdout.
Parameters: None – Returns: Nothing
-
classmethod
rpyc_list_deployed_servers
()¶
-
classmethod
rpyc_check_connection
(host, user=None, instance=1)¶ Check whether a connection is open or closed.
Parameters: - host (str) – The hostname or IP of the remote system.
- user (str) – A user on the remote system. Default: root
- instance (int) – The number of the instance when multiple connections are used.
Returns: Nothing
-
classmethod
rpyc_ping_connection
(host, user=None, instance=1)¶ Ping an rpyc connection.
Parameters: - host (str) – The hostname or IP of the remote system.
- user (str) – A user on the remote system. Default: root
- instance (int) – The number of the instance when multiple connections are used.
Returns: True if pingable. False if does not ping.
-
classmethod
rpyc_close_connection
(host=None, user=None, instance=1)¶ Close an rpyc connection.
Parameters: - host (str) – The hostname or IP of the remote system.
- user (str) – A user on the remote system. Default: root
- instance (int) – The number of the instance when multiple connections are used.
Returns: Nothing.
-
classmethod
rpyc_close_connections
()¶ Close all rpyc connections.
Parameters: None – Returns: Nothing
-
classmethod
rpyc_close_deployed_servers
()¶ Close all deployed server connections.
Parameters: None – Returns: Nothing
-
classmethod
rpyc_close_deployed_server
(host=None, user=None)¶ Close a deployed server connection.
Parameters: - host (str) – The hostname or IP of the remote system.
- user (str) – A user on the remote system. Default: root
Returns: Nothing.
-
classmethod
rpyc_define_module
(connection, local_module)¶ Define a local module on the remote system
Parameters: - connection (obj) – An rpyc connection object.
- local_module (obj) – The module object being defined on the remote.
Returns: A module object representing the local module defined on remote
-
classmethod
glusto.templatable module¶
All things Jinja templates.
Note
Templatable is inherited by the Glusto class and not designed to be instantiated.
-
class
glusto.templatable.
Templatable
¶ Bases:
object
The class providing Jinja template functionality.
-
static
render_template
(template_filename, template_vars={}, output_file=None, searchpath=None)¶ Render a template into text file
Parameters: - template_filename (str) – Fully qualified template filename.
- template_vars (dict) – A dictionary of variables.
- output_file (str) – Fully qualified output filename.
- searchpath (str) – The root path to begin file searches. Default is the current path.
Returns: String containing template rendering result if successful.
-
static
glusto.unittestable module¶
All things unittest.
Note
Unittestable is inherited by the Glusto class and not designed to be instantiated.
-
class
glusto.unittestable.
Unittestable
¶ Bases:
object
The class providing unittest functionality.
-
static
load_tests
(test_class, loader, ordered_testcases)¶ - Load specified tests in a order followed by the remaining tests in
- the test_class.
Parameters: - test_class (object) – The TestCase class object with test methods.
- loader (object) – The loader object passed from unittests to calling load_tests function.
- ordered_testcases (list) – list of testcase method names in order to be run.
Returns: Returns a unittest.TestSuite() containing loaded tests.
Note
This feature requires Python2.7 or higher
-
static