testinfra DocumentationRelease 1.5.5
Philippe Pepiot
Jun 30, 2017
Contents
1 About 3
2 Quick start 5
3 Documentation 73.1 Changelog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
3.1.1 1.5.5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73.1.2 1.5.4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73.1.3 1.5.3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73.1.4 1.5.2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73.1.5 1.5.1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73.1.6 1.5.0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83.1.7 1.4.5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
3.2 Invocation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83.2.1 Test multiples hosts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83.2.2 Parallel execution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83.2.3 Advanced invocation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
3.3 Connection backends . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93.3.1 local . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93.3.2 paramiko . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93.3.3 docker . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93.3.4 ssh . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103.3.5 salt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103.3.6 ansible . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103.3.7 kubectl . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
3.4 Modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103.4.1 Command . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113.4.2 LocalCommand . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113.4.3 TestinfraBackend . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113.4.4 Sudo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123.4.5 File . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123.4.6 User . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143.4.7 Group . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153.4.8 Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153.4.9 Package . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153.4.10 PipPackage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163.4.11 Process . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
i
3.4.12 Service . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173.4.13 Supervisor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173.4.14 Socket . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183.4.15 SystemInfo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193.4.16 Salt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193.4.17 Ansible . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193.4.18 PuppetResource . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203.4.19 Facter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203.4.20 Sysctl . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213.4.21 MountPoint . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
3.5 API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223.5.1 Connection API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
3.6 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223.6.1 Parametrize your tests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223.6.2 Using unittest . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233.6.3 Make your own modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233.6.4 Share your modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243.6.5 Integration with vagrant . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243.6.6 Integration with jenkins . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243.6.7 Integration with nagios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243.6.8 Test docker images . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
3.7 Support . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273.7.1 Issue Tracker . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273.7.2 IRC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273.7.3 pytest documentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273.7.4 Community Contributions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
ii
testinfra Documentation, Release 1.5.5
Latest documentation: https://testinfra.readthedocs.io/en/latest
Contents 1
testinfra Documentation, Release 1.5.5
2 Contents
CHAPTER 1
About
With Testinfra you can write unit tests in Python to test actual state of your servers configured by managements toolslike Salt, Ansible, Puppet, Chef and so on.
Testinfra aims to be a Serverspec equivalent in python and is written as a plugin to the powerful Pytest test engine
3
testinfra Documentation, Release 1.5.5
4 Chapter 1. About
CHAPTER 2
Quick start
Install testinfra using pip:
$ pip install testinfra
# or install the devel version$ pip install 'git+https://github.com/philpep/testinfra@master#egg=testinfra'
Write your first tests file to test_myinfra.py:
def test_passwd_file(File):passwd = File("/etc/passwd")assert passwd.contains("root")assert passwd.user == "root"assert passwd.group == "root"assert passwd.mode == 0o644
def test_nginx_is_installed(Package):nginx = Package("nginx")assert nginx.is_installedassert nginx.version.startswith("1.2")
def test_nginx_running_and_enabled(Service):nginx = Service("nginx")assert nginx.is_runningassert nginx.is_enabled
And run it:
$ testinfra -v test_myinfra.py
====================== test session starts ======================platform linux -- Python 2.7.3 -- py-1.4.26 -- pytest-2.6.4
5
testinfra Documentation, Release 1.5.5
plugins: testinfracollected 3 items
test_myinfra.py::test_passwd_file[local] PASSEDtest_myinfra.py::test_nginx_is_installed[local] PASSEDtest_myinfra.py::test_nginx_running_and_enabled[local] PASSED
=================== 3 passed in 0.66 seconds ====================
6 Chapter 2. Quick start
CHAPTER 3
Documentation
Changelog
1.5.5
• backends: Fix ansible backend with ansible >= 2.3 (#195)
1.5.4
• backends: fallback to UTF-8 encoding when system encoding is ASCII.
• Service: fix is_running() on systems using Upstart
1.5.3
• Sudo: restore backend command in case of exceptions
1.5.2
• Honnor become_user when using the ansible backend
1.5.1
• Add dependency on importlib on python 2.6
7
testinfra Documentation, Release 1.5.5
1.5.0
• New kubectl backend
• Command: check_output strip carriage return and newlines (#164)
• Package: rpm improve getting version() and release()
• User: add gecos (comment) field (#155)
1.4.5
• SystemInfo: detect codename from VERSION_CODENAME in /etc/os-release (fallback when lsb_release isn’tinstalled).
• Package: add release property for rpm based systems.
Invocation
Test multiples hosts
By default Testinfra launch tests on local machine, but you can also test remotes systems using paramiko (a sshimplementation in python):
$ pip install paramiko$ testinfra -v --hosts=localhost,root@webserver:2222 test_myinfra.py
====================== test session starts ======================platform linux -- Python 2.7.3 -- py-1.4.26 -- pytest-2.6.4plugins: testinfracollected 3 items
test_myinfra.py::test_passwd_file[localhost] PASSEDtest_myinfra.py::test_nginx_is_installed[localhost] PASSEDtest_myinfra.py::test_nginx_running_and_enabled[localhost] PASSEDtest_myinfra.py::test_passwd_file[root@webserver:2222] PASSEDtest_myinfra.py::test_nginx_is_installed[root@webserver:2222] PASSEDtest_myinfra.py::test_nginx_running_and_enabled[root@webserver:2222] PASSED
=================== 6 passed in 8.49 seconds ====================
You can also set hosts per test module:
testinfra_hosts = ["localhost", "root@webserver:2222"]
def test_foo(File, Package, Service):[....]
Parallel execution
If you have a lot of tests, you can use the pytest-xdist plugin to run tests using multiples process:
8 Chapter 3. Documentation
testinfra Documentation, Release 1.5.5
$ pip install pytest-xdist
# Launch tests using 3 processes$ testinfra -n 3 -v --host=web1,web2,web3,web4,web5,web6 test_myinfra.py
Advanced invocation
# Test recursively all test files (starting with `test_`) in current directory$ testinfra
# Filter function/hosts with pytest -k option$ testinfra --hosts=webserver,dnsserver -k webserver -k nginx
For more usages and features, see the Pytest documentation.
Connection backends
Testinfra comes with several connections backends for remote command execution, they are controlled with the--connection parameter.
For all backends, command can be run as superuser with the --sudo option or as specific user by adding a--sudo-user option.
local
This is the default backend when not hosts are provided (either via --hosts or in modules). Commands are runlocally in a subprocess under the current user:
$ testinfra --sudo test_myinfra.py
paramiko
This is the default backend when a hosts list is provided, paramiko is a python implementation of SSHv2 protocol.Testinfra will not ask you for a password, so you must be able to connect without password (using password less keysor using ssh-agent).
You can provide an alternate ssh-config and use sudo on the remote host:
$ testinfra --ssh-config=/path/to/ssh_config --sudo --hosts=server
docker
The docker backend can be used to test running containers. It use the docker exec command:
$ testinfra --connection=docker --hosts=[user@]docker_id_or_name
See also the Test docker images example.
3.3. Connection backends 9
testinfra Documentation, Release 1.5.5
ssh
This is a pure ssh backend using the ssh command available in $PATH. Example:
$ testinfra --connection=ssh --hosts=server
The ssh backend also accept --ssh-config and --sudo parameters.
salt
The salt backend use the salt python client API and can be used from the salt-master server:
$ testinfra --connection=salt # equivalent to --hosts='*'$ testinfra --connection=salt --hosts=minion1,minion2$ testinfra --connection=salt --hosts='web*'$ testinfra --connection=salt --hosts=G@os:Debian
Testinfra will use the salt connection channel to run commands.
Host can be seleted using glob or compound matchers.
ansible
The ansible backend use the ansible python API:
$ testinfra --connection=ansible # tests all inventory hosts$ testinfra --connection=ansible --hosts=host1,host2$ testinfra --connection=ansible --hosts='web*'
You can use an alternative inventory with the --ansible-inventory option.
Note: Ansible settings such as remote_user, etc., may be configured by using Ansible’s environment variables.
kubectl
The kubectl backend can be used to test containers running in Kubernetes. It use the kubectl exec command:
$ testinfra --connection=kubectl --hosts=pod_id-123456789-9fng/container_name
Modules
Testinfra modules are provided as pytest fixtures, declare them as arguments of your test function to make themavailable:
def test_foo(Package, File, Command):[...]
Note that you can also Using unittest.
10 Chapter 3. Documentation
testinfra Documentation, Release 1.5.5
Command
class testinfra.modules.command.Command(command, *args)Run given command and return rc (exit status), stdout and stderr
>>> cmd = Command("ls -l /etc/passwd")>>> cmd.rc0>>> cmd.stdout'-rw-r--r-- 1 root root 1790 Feb 11 00:28 /etc/passwd\n'>>> cmd.stderr''
Good practice: always use shell arguments quoting to avoid shell injection
>>> cmd = Command("ls -l -- %s", "/;echo inject")CommandResult(
rc=2, stdout='',stderr='ls: cannot access /;echo inject: No such file or directory\n',command="ls -l '/;echo inject'")
exists(command)Return True if given command exist in $PATH
check_output(command, *args, **kwargs)Get stdout of a command which has run successfully
Returns stdout without trailing newline
Raises AssertionError
run_expect(expected, command, *args, **kwargs)Run command and check it return an expected exit status
Parameters expected – A list of expected exit status
Raises AssertionError
run_test(command, *args, **kwargs)Run command and check it return an exit status of 0 or 1
Raises AssertionError
LocalCommand
testinfra.plugin.LocalCommand(command, *args)Run commands locally
Same as Command but run commands locally with subprocess even when the connection backend is not “local”.
Note: LocalCommand does NOT respect --sudo option
TestinfraBackend
class testinfra.backend.base.BaseBackendRepresent the connection to the remote or local system
3.4. Modules 11
testinfra Documentation, Release 1.5.5
classmethod get_connection_type()Return the connection backend used as string.
Can be local, paramiko, ssh, docker, salt or ansible
get_hostname()Return the hostname (for testinfra) of the remote or local system
Can be useful for multi-hosts tests:
Example:
import requests
def test(TestinfraBackend):host = TestinfraBackend.get_hostname()response = requests.get("http://" + host)assert response.status_code == 200
$ testinfra --hosts=server1,server2 test.py
test.py::test[paramiko://server1] PASSEDtest.py::test[paramiko://server2] PASSED
get_module(name)Return the testinfra module adapted to the current backend
def test(Package):[...]
# Is equivalent todef test(TestinfraBackend):
Package = TestinfraBackend.get_module("Package")
Sudo
class testinfra.modules.sudo.Sudo(user=None)Sudo module allow to run certain portion of code under another user.
It is used as a context manager and can be nested.
>>> Command.check_output("whoami")'phil'>>> with Sudo():... Command.check_output("whoami")... with Sudo("www-data"):... Command.check_output("whoami")...'root''www-data'
File
class testinfra.modules.file.File(path)Test various files attributes
12 Chapter 3. Documentation
testinfra Documentation, Release 1.5.5
existsTest if file exists
>>> File("/etc/passwd").existsTrue>>> File("/nonexistent").existsFalse
is_file
is_directory
is_pipe
is_socket
is_symlink
linked_toResolve symlink
>>> File("/var/lock").linked_to'/run/lock'
userReturn file owner as string
>>> File("/etc/passwd").user'root'
uidReturn file user id as integer
>>> File("/etc/passwd").uid0
group
gid
modeReturn file mode as octal integer
>>> File("/etc/passwd").mode384 # 0o600 (octal)>>> File("/etc/password").mode == 0o600True>>> oct(File("/etc/password").mode) == '0600'True
Note: Python 3 oct(x)_ function will produce '0o600'
You can also utilize the file mode constants from the stat library for testing file mode.
>>> import stat>>> File("/etc/password").mode == stat.S_IRUSR | stat.S_IWUSRTrue
contains(pattern)
md5sum
3.4. Modules 13
testinfra Documentation, Release 1.5.5
sha256sum
contentReturn file content as bytes
>>> File("/tmp/foo").contentb'caf\xc3\xa9'
content_stringReturn file content as string
>>> File("/tmp/foo").content_string'café'
mtimeReturn time of last modification as datetime.datetime object
>>> File("/etc/passwd").mtimedatetime.datetime(2015, 3, 15, 20, 25, 40)
sizeReturn size of file in bytes
User
class testinfra.modules.user.User(name=None)Test unix users
If name is not supplied, test the current user
nameReturn user name
exists
uidReturn user ID
gidReturn effective group ID
groupReturn effective group name
gidsReturn the list of user group IDs
groupsReturn the list of user group names
homeReturn the user home directory
shellReturn the user login shell
passwordReturn the crypted user password
gecosReturn the user comment/gecos field
14 Chapter 3. Documentation
testinfra Documentation, Release 1.5.5
expiration_dateReturn the account expiration date
>>> User("phil").expiration_datedatetime.datetime(2020, 1, 1, 0, 0)>>> User("root").expiration_dateNone
classmethod get_module_class(_backend)
Group
class testinfra.modules.group.Group(name=None)Test unix group
exists
gid
Interface
class testinfra.modules.interface.Interface(name)Test network interfaces
exists
speed
addressesReturn ipv4 and ipv6 addresses on the interface
>>> Interface("eth0").addresses['192.168.31.254', '192.168.31.252', 'fe80::e291:f5ff:fe98:6b8c']
Package
class testinfra.modules.package.Package(name)Test packages status and version
is_installedTest if the package is installed
>>> Package("nginx").is_installedTrue
Supported package systems:
•apt (Debian, Ubuntu, ...)
•rpm (RHEL, Centos, Fedora, ...)
•pkg_info (OpenBSD)
•pkg_info (NetBSD)
•pkg (FreeBSD)
3.4. Modules 15
testinfra Documentation, Release 1.5.5
releaseReturn the release specific info from the package version
>>> Package("nginx").release'1.el6'
versionReturn package version as returned by the package system
>>> Package("nginx").version'1.2.1-2.2+wheezy3'
PipPackage
class testinfra.modules.pip.PipPackageTest pip packages status and version
get_packages(pip_path=u’pip’)Get all installed packages and versions returned by pip list:
>>> PipPackage.get_packages(pip_path='~/venv/website/bin/pip'){'Django': {'version': '1.10.2'},'mywebsite': {'version': '1.0a3', 'path': '/srv/website'},'psycopg2': {'version': '2.6.2'}}
get_outdated_packages(pip_path=u’pip’)Get all outdated packages with current and latest version
>>> PipPackage.get_outdated_packages(pip_path='~/venv/website/bin/pip'){'Django': {'current': '1.10.2', 'latest': '1.10.3'}}
Process
class testinfra.modules.process.ProcessTest Processes attributes
Processes are selected using filter() or get(), attributes names are described in the ps(1) man page.
>>> master = Process.get(user="root", comm="nginx")# Here is the master nginx process (running as root)>>> master.args'nginx: master process /usr/sbin/nginx -g daemon on; master_process on;'# Here are the worker processes (Parent PID = master PID)>>> workers = Process.filter(ppid=master.pid)>>> len(workers)4# Nginx don't eat memory>>> sum([w.pmem for w in workers])0.8# But php does !>>> sum([p.pmem for p in Process.filter(comm="php5-fpm")])19.2
filter(**filters)Get a list of matching process
16 Chapter 3. Documentation
testinfra Documentation, Release 1.5.5
>>> Process.filter(user="root", comm="zsh")[<process zsh (pid=2715)>, <process zsh (pid=10502)>, ...]
get(**filters)Get one matching process
Raise RuntimeError if no process found or multiple process matching filters.
Service
class testinfra.modules.service.Service(name)Test services
Implementations:
•Linux: detect Systemd or Upstart, fallback to SysV
•FreeBSD: service(1)
•OpenBSD: /etc/rc.d/$name check for is_running (is_enabled is not yet implemented)
•NetBSD: /etc/rc.d/$name onestatus for is_running (is_enabled is not yet imple-mented)
is_runningTest if service is running
is_enabledTest if service is enabled
Supervisor
class testinfra.modules.supervisor.Supervisor(name, _attrs_cache=None)Test supervisor managed services
>>> gunicorn = Supervisor("gunicorn")>>> gunicorn.status'RUNNING'>>> gunicorn.is_runningTrue>>> gunicorn.pid4242
is_runningReturn True if managed service is in status RUNNING
statusReturn the status of the managed service
Status can be STOPPED, STARTING, RUNNING, BACKOFF, STOPPING, EXITED, FATAL, UN-KNOWN.
See http://supervisord.org/subprocess.html#process-states
pidReturn the pid (as int) of the managed service
classmethod get_services()Get a list of services running under supervisor
3.4. Modules 17
testinfra Documentation, Release 1.5.5
>>> Supervisor.get_services()[<Supervisor(name="gunicorn", status="RUNNING", pid=4232)><Supervisor(name="celery", status="FATAL", pid=None)>]
Socket
class testinfra.modules.socket.Socket(socketspec)Test listening tcp/udp and unix sockets
socketspec must be specified as <protocol>://<host>:<port>
This module requires the netstat command to on the target host.
Example:
•Unix sockets: unix:///var/run/docker.sock
•All ipv4 and ipv6 tcp sockets on port 22: tcp://22
•All ipv4 sockets on port 22: tcp://0.0.0.0:22
•All ipv6 sockets on port 22: tcp://:::22
•udp socket on 127.0.0.1 port 69: udp://127.0.0.1:69
is_listeningTest if socket is listening
>>> Socket("unix:///var/run/docker.sock").is_listeningFalse>>> # This HTTP server listen on all ipv4 adresses but not on ipv6>>> Socket("tcp://0.0.0.0:80").is_listeningTrue>>> Socket("tcp://:::80").is_listeningFalse>>> Socket("tcp://80").is_listeningFalse
Note: If you don’t specify a host for udp and tcp sockets, then the socket is listening if and only if thesocket listen on both all ipv4 and ipv6 addresses (ie 0.0.0.0 and ::)
clientsReturn a list of clients connected to a listening socket
For tcp and udp sockets a list of pair (adress, port) is returned. For unix sockets a list of None is returned(thus you can make a len() for counting clients).
>>> Socket("tcp://22").clients[('2001:db8:0:1', 44298), ('192.168.31.254', 34866)]>>> Socket("unix:///var/run/docker.sock")[None, None, None]
classmethod get_listening_sockets()Return a list of all listening sockets
>>> Socket.get_listening_sockets()['tcp://0.0.0.0:22', 'tcp://:::22', 'unix:///run/systemd/private', ...]
18 Chapter 3. Documentation
testinfra Documentation, Release 1.5.5
SystemInfo
class testinfra.modules.systeminfo.SystemInfoReturn system informations
typeOS type
>>> SystemInfo.type'linux'
distributionDistribution name
>>> SystemInfo.distribution'debian'
releaseDistribution release number
>>> SystemInfo.release'7.8'
codenameRelease code name
>>> SystemInfo.codename'wheezy'
Salt
class testinfra.modules.salt.Salt(function, args=None)Run salt module functions
>>> Salt("pkg.version", "nginx")'1.6.2-5'>>> Salt("pkg.version", ["nginx", "php5-fpm"]){'nginx': '1.6.2-5', 'php5-fpm': '5.6.7+dfsg-1'}>>> Salt("grains.item", ["osarch", "mem_total", "num_cpus"]){'osarch': 'amd64', 'num_cpus': 4, 'mem_total': 15520}
Run salt-call sys.doc to get a complete list of functions
Ansible
class testinfra.modules.ansible.Ansible(module_name, module_args=None, check=True)Run Ansible module functions
This module is only available with the ansible connection backend.
Check mode is enabled by default, you can disable it with check=False.
>>> Ansible("apt", "name=nginx state=present")["changed"]False>>> Ansible("command", "echo foo", check=False)["stdout"]'foo'
3.4. Modules 19
testinfra Documentation, Release 1.5.5
>>> Ansible("setup")["ansible_facts"]["ansible_lsb"]["codename"]'jessie'>>> Ansible("file", "path=/etc/passwd")["mode"]'0640'
exception AnsibleException(result)Exception raised when an error occur in an ansible call
result from ansible can be accessed through the result attribute
>>> try:... Ansible("command", "echo foo")... except Ansible.AnsibleException as exc:... assert exc.result['failed'] is True... assert exc.result['msg'] == 'check mode not supported for command'
Ansible.get_variables()Returns a dict of ansible variables
>>> Ansible.get_variables(){
'inventory_hostname': 'localhost','group_names': ['ungrouped'],'foo': 'bar',
}
PuppetResource
class testinfra.modules.puppet.PuppetResource(type, name=None)Get puppet resources
Run puppet resource --types to get a list of available types.
>>> PuppetResource("user", "www-data"){
'www-data': {'ensure': 'present','comment': 'www-data','gid': '33','home': '/var/www','shell': '/usr/sbin/nologin','uid': '33',
},}
Facter
class testinfra.modules.puppet.Facter(*facts)Get facts with facter
>>> Facter(){
"operatingsystem": "Debian","kernel": "linux",[...]
20 Chapter 3. Documentation
testinfra Documentation, Release 1.5.5
}>>> Facter("kernelversion", "is_virtual"){"kernelversion": "3.16.0","is_virtual": "false"
}
Sysctl
class testinfra.modules.sysctl.Sysctl(name)Test kernel parameters
>>> Sysctl("kernel.osrelease")"3.16.0-4-amd64">>> Sysctl("vm.dirty_ratio")20
MountPoint
class testinfra.modules.mountpoint.MountPoint(path)Test Mount Points
existsReturn True if the mountpoint exists
>>> MountPoint("/").existsTrue
>>> MountPoint("/not/a/mountpoint").existsFalse
filesystemReturns the filesystem type associated
>>> MountPoint("/").filesystem'ext4'
deviceReturn the device associated
>>> MountPoint("/").device'/dev/sda1'
optionsReturn a list of options that a mount point has been created with
>>> MountPoint("/").options['rw', 'relatime', 'data=ordered']
classmethod get_mountpoints()Returns a list of MountPoint instances
3.4. Modules 21
testinfra Documentation, Release 1.5.5
>>> MountPoint.get_mountpoints()[<MountPoint(path=/proc, device=proc, filesystem=proc, options=rw,nosuid,→˓nodev,noexec,relatime)><MountPoint(path=/, device=/dev/sda1, filesystem=ext4, options=rw,relatime,→˓errors=remount-ro,data=ordered)>]
API
Connection API
You can use testinfra outside of pytest or you can dynamically get a connection and call fonction from modules:
>>> import testinfra>>> conn = testinfra.get_backend("paramiko://root@server:2222", sudo=True)>>> conn.File("/etc/shadow").mode == 0o640True
Same applies to all Modules.
For instance you could make a test to compare two files on two different servers:
import testinfra
def test_same_passwd():a = testinfra.get_backend("ssh://a")b = testinfra.get_backend("ssh://b")assert a.File("/etc/passwd").content == a.File("/etc/passwd").content
Examples
Parametrize your tests
Pytest support test parametrization:
# BAD: If the test fails on nginx, python is not testeddef test_packages(Package):
for name, version in (("nginx", "1.6"),("python", "2.7"),
):assert Package(name).is_installedassert Package(name).version.startswith(version)
# GOOD: Each package is tested# $ testinfra -v test.py# [...]# test.py::test_package[local-nginx-1.6] PASSED# test.py::test_package[local-python-2.7] PASSED# [...]import pytest
22 Chapter 3. Documentation
testinfra Documentation, Release 1.5.5
@pytest.mark.parametrize("name,version", [("nginx", "1.6"),("python", "2.7"),
])def test_packages(Package, name, version):
assert Package(name).is_installedassert Package(name).version.startswith(version)
Using unittest
Testinfra can be used with python standard unit test framework unittest instead of pytest:
import unittestimport testinfra
class Test(unittest.TestCase):
def setUp(self):self.b = testinfra.get_backend("paramiko://root@host")
def test_nginx_config(self):self.assertEqual(self.b.Command("nginx -t").rc, 0)
def test_nginx_service(self):service = self.b.Service("nginx")self.assertTrue(service.is_running)self.assertTrue(service.is_enabled)
if __name__ == "__main__":unittest.main()
$ python test.py..----------------------------------------------------------------------Ran 2 tests in 0.705s
OK
Make your own modules
Suppose you want to create a simple wrapper around the echo command. You just have to declare a pytest fixture:
import pytest
@pytest.fixture()def Echo(Command):
def f(arg):return Command.check_output("echo %s", arg)
return f
3.6. Examples 23
testinfra Documentation, Release 1.5.5
def test(Echo):assert Echo("foo") == "foo"
If you want to use it in all your test file, just put it in a conftest.py file.
Share your modules
Suppose you wrote a more useful module than the echo wrapper above and want to share with the entire world. Youcan package your plugin as a pytest plugin.
See philpep/testinfra-echo to see an example of pytest plugin based on testinfra.
Integration with vagrant
Vagrant is a tool that setup and provision development environment (virtual machines).
When your vagrant machine is up and running, you can easily run your testinfra test suite on it:
vagrant ssh-config > .vagrant/ssh-configtestinfra --hosts=default --ssh-config=.vagrant/ssh-config tests.py
Integration with jenkins
Jenkins is a well known open source continuous integration server.
If your jenkins slave can run vagrant, your build scripts can be like:
pip install testinfra paramikovagrant upvagrant ssh-config > .vagrant/ssh-configtestinfra --hosts=default --ssh-config=.vagrant/ssh-config --junit-xml junit.xml→˓tests.py
Then configure jenkins to get tests results from the junit.xml file.
If you use the docker provisioner in vagrant, and use the docker plugin in jenkins, you might be interested by thephilpep/jenkins-slave:jessie docker image. This is the image used to tests testinfra itself using vagrant and docker (indocker).
Integration with nagios
The tests you will write with testinfra will usually be testing that the services you’re deploying run correctly. This kindof tests are close to monitoring checks, so let’s push them to Nagios !
Testinfra has an option –nagios that enable a compatible nagios plugin beharvior:
$ testinfra -qq --nagios test_ok.py; echo $?TESTINFRA OK - 2 passed, 0 failed, 0 skipped in 2.30 seconds[...]0
$ testinfra -qq --nagios test_fail.py; echo $?TESTINFRA CRITICAL - 1 passed, 1 failed, 0 skipped in 2.24 seconds
24 Chapter 3. Documentation
testinfra Documentation, Release 1.5.5
[Traceback that explain the failed test]2
You can run these tests from the nagios master or in the target host with NRPE.
Test docker images
Docker is a handy way to test your infrastructure code. This recipe show how to test the resulting docker image withtestinfra and provides awesome features like testing multiple images and run some destructive tests on a dedicatedcontainer.
This will use advanced pytest features, to understand the underlying concepts read the pytest documentation:
• https://pytest.org/latest/fixture.html
• https://pytest.org/latest/parametrize.html
• https://pytest.org/latest/example/markers.html
Put this code in a conftest.py file:
import pytestimport testinfra
# Use testinfra to get a handy function to run commands locallycheck_output = testinfra.get_backend(
"local://").get_module("Command").check_output
@pytest.fixturedef TestinfraBackend(request):
# Override the TestinfraBackend fixture,# all testinfra fixtures (i.e. modules) depend on it.
docker_id = check_output("docker run -d %s tail -f /dev/null", request.param)
def teardown():check_output("docker rm -f %s", docker_id)
# Destroy the container at the end of the fixture liferequest.addfinalizer(teardown)
# Return a dynamic created backendreturn testinfra.get_backend("docker://" + docker_id)
def pytest_generate_tests(metafunc):if "TestinfraBackend" in metafunc.fixturenames:
# Lookup "docker_images" markermarker = getattr(metafunc.function, "docker_images", None)if marker is not None:
images = marker.argselse:
# Default imageimages = ["debian:jessie"]
3.6. Examples 25
testinfra Documentation, Release 1.5.5
# If the test has a destructive marker, we scope TestinfraBackend# at function level (i.e. executing for each test). If not we scope# at session level (i.e. all tests will share the same container)if getattr(metafunc.function, "destructive", None) is not None:
scope = "function"else:
scope = "session"
metafunc.parametrize("TestinfraBackend", images, indirect=True, scope=scope)
Then create a test_docker.py file with our testinfra tests:
import pytest
# To mark all the tests as destructive:# pytestmark = pytest.mark.destructive
# To run all the tests on given docker images:# pytestmark = pytest.mark.docker_images("debian:jessie", "centos:7")
# Both# pytestmark = [# pytest.mark.destructive,# pytest.mark.docker_images("debian:jessie", "centos:7")# ]
# This test will run on default image (debian:jessie)def test_default(Process):
assert Process.get(pid=1).comm == "tail"
# This test will run on both debian:jessie and centos:7 [email protected]_images("debian:jessie", "centos:7")def test_multiple(Process):
assert Process.get(pid=1).comm == "tail"
# This test is marked as destructive and will run on its own container# It will create a /foo file and run 3 times with different [email protected]@pytest.mark.parametrize("content", ["bar", "baz", "qux"])def test_destructive(Command, File, content):
assert not File("/foo").existsCommand.check_output("echo %s > /foo", content)assert File("/foo").content_string == content + "\n"
Now let’s run it:
$ testinfra -v[...]
test_docker.py::test_default[debian:jessie] PASSEDtest_docker.py::test_multiple[debian:jessie] PASSEDtest_docker.py::test_multiple[centos:7] PASSEDtest_docker.py::test_destructive[debian:jessie-bar] PASSED
26 Chapter 3. Documentation
testinfra Documentation, Release 1.5.5
test_docker.py::test_destructive[debian:jessie-baz] PASSEDtest_docker.py::test_destructive[debian:jessie-qux] PASSED
Note that you can speedup the tests execution by using pytest-xdist.
Support
If you have questions or need help with testinfra please consider one of the following
Issue Tracker
Checkout existing issues on project issue tracker
IRC
You can also ask questions on IRC in #testinfra channel on Freenode
pytest documentation
testinfra is implemented as pytest plugin so to get the most out of please read pytest documentation
Community Contributions
• Molecule is an Automated testing framework for Ansible roles, with native Testinfra support.
3.7. Support 27
testinfra Documentation, Release 1.5.5
28 Chapter 3. Documentation
Index
Aaddresses (testinfra.modules.interface.Interface attribute),
15Ansible (class in testinfra.modules.ansible), 19Ansible.AnsibleException, 20
BBaseBackend (class in testinfra.backend.base), 11
Ccheck_output() (testinfra.modules.command.Command
method), 11clients (testinfra.modules.socket.Socket attribute), 18codename (testinfra.modules.systeminfo.SystemInfo at-
tribute), 19Command (class in testinfra.modules.command), 11contains() (testinfra.modules.file.File method), 13content (testinfra.modules.file.File attribute), 14content_string (testinfra.modules.file.File attribute), 14
Ddevice (testinfra.modules.mountpoint.MountPoint at-
tribute), 21distribution (testinfra.modules.systeminfo.SystemInfo at-
tribute), 19
Eexists (testinfra.modules.file.File attribute), 12exists (testinfra.modules.group.Group attribute), 15exists (testinfra.modules.interface.Interface attribute), 15exists (testinfra.modules.mountpoint.MountPoint at-
tribute), 21exists (testinfra.modules.user.User attribute), 14exists() (testinfra.modules.command.Command method),
11expiration_date (testinfra.modules.user.User attribute), 15
FFacter (class in testinfra.modules.puppet), 20
File (class in testinfra.modules.file), 12filesystem (testinfra.modules.mountpoint.MountPoint at-
tribute), 21filter() (testinfra.modules.process.Process method), 16
Ggecos (testinfra.modules.user.User attribute), 14get() (testinfra.modules.process.Process method), 17get_connection_type() (testin-
fra.backend.base.BaseBackend class method),11
get_hostname() (testinfra.backend.base.BaseBackendmethod), 12
get_listening_sockets() (testinfra.modules.socket.Socketclass method), 18
get_module() (testinfra.backend.base.BaseBackendmethod), 12
get_module_class() (testinfra.modules.user.User classmethod), 15
get_mountpoints() (testin-fra.modules.mountpoint.MountPoint classmethod), 21
get_outdated_packages() (testin-fra.modules.pip.PipPackage method), 16
get_packages() (testinfra.modules.pip.PipPackagemethod), 16
get_services() (testinfra.modules.supervisor.Supervisorclass method), 17
get_variables() (testinfra.modules.ansible.Ansiblemethod), 20
gid (testinfra.modules.file.File attribute), 13gid (testinfra.modules.group.Group attribute), 15gid (testinfra.modules.user.User attribute), 14gids (testinfra.modules.user.User attribute), 14Group (class in testinfra.modules.group), 15group (testinfra.modules.file.File attribute), 13group (testinfra.modules.user.User attribute), 14groups (testinfra.modules.user.User attribute), 14
29
testinfra Documentation, Release 1.5.5
Hhome (testinfra.modules.user.User attribute), 14
IInterface (class in testinfra.modules.interface), 15is_directory (testinfra.modules.file.File attribute), 13is_enabled (testinfra.modules.service.Service attribute),
17is_file (testinfra.modules.file.File attribute), 13is_installed (testinfra.modules.package.Package at-
tribute), 15is_listening (testinfra.modules.socket.Socket attribute),
18is_pipe (testinfra.modules.file.File attribute), 13is_running (testinfra.modules.service.Service attribute),
17is_running (testinfra.modules.supervisor.Supervisor at-
tribute), 17is_socket (testinfra.modules.file.File attribute), 13is_symlink (testinfra.modules.file.File attribute), 13
Llinked_to (testinfra.modules.file.File attribute), 13LocalCommand() (in module testinfra.plugin), 11
Mmd5sum (testinfra.modules.file.File attribute), 13mode (testinfra.modules.file.File attribute), 13MountPoint (class in testinfra.modules.mountpoint), 21mtime (testinfra.modules.file.File attribute), 14
Nname (testinfra.modules.user.User attribute), 14
Ooptions (testinfra.modules.mountpoint.MountPoint
attribute), 21
PPackage (class in testinfra.modules.package), 15password (testinfra.modules.user.User attribute), 14pid (testinfra.modules.supervisor.Supervisor attribute), 17PipPackage (class in testinfra.modules.pip), 16Process (class in testinfra.modules.process), 16PuppetResource (class in testinfra.modules.puppet), 20
Rrelease (testinfra.modules.package.Package attribute), 15release (testinfra.modules.systeminfo.SystemInfo at-
tribute), 19run_expect() (testinfra.modules.command.Command
method), 11
run_test() (testinfra.modules.command.Commandmethod), 11
SSalt (class in testinfra.modules.salt), 19Service (class in testinfra.modules.service), 17sha256sum (testinfra.modules.file.File attribute), 13shell (testinfra.modules.user.User attribute), 14size (testinfra.modules.file.File attribute), 14Socket (class in testinfra.modules.socket), 18speed (testinfra.modules.interface.Interface attribute), 15status (testinfra.modules.supervisor.Supervisor attribute),
17Sudo (class in testinfra.modules.sudo), 12Supervisor (class in testinfra.modules.supervisor), 17Sysctl (class in testinfra.modules.sysctl), 21SystemInfo (class in testinfra.modules.systeminfo), 19
Ttype (testinfra.modules.systeminfo.SystemInfo attribute),
19
Uuid (testinfra.modules.file.File attribute), 13uid (testinfra.modules.user.User attribute), 14User (class in testinfra.modules.user), 14user (testinfra.modules.file.File attribute), 13
Vversion (testinfra.modules.package.Package attribute), 16
30 Index