Managing Systems and Applications with pyinfra

Using Host Data

You can also assign data to individual hosts. For example, you might assign a list of packages to install on each host. This is done by using a tuple with the hostname and a dictionary of data in the group list in the inventory file (inventory.py), as illustrated in Listing 1.

Listing 1

inventory.py with Host Data

01 servers = [
02   ("podpile.home", {"packages": ["tmux", "vim"]}),
03   ("code.example.com", {"packages": ["tmux"]})
04 ]
05 workstations = [
06   ("nov.home", {"packages": ["git", "tmux", "vim", "taskwarrior"]}),
07   ("tux.home", {"packages": ["git"]})
08 ]

With this host data in the inventory, the deploy file (deploy.py) can be made more generic, referring to the packages variable as host.data.packages, as in Listing 2. When you run this deploy, pyinfra installs a specific list of packages on each host.

Listing 2

deploy.py to Install Packages

01 from pyinfra import host
02 from pyinfra.operations import server
03
04 server.packages(
05   name="Install packages", packages=host.data.packages, _sudo=True
06 )

Using Group Data

Of course, you create groups of your hosts because they share common features. Therefore, pyinfra allows you to assign data to a group. For instance, if you want to install tmux and Vim on all your servers, you can define these in group_data/servers.py with

packages = ["tmux", "vim"]

And then, to install Git, tmux, Vim, and Taskwarrior on all your workstations, you would define them in group/data/workstations.py like this:

packages = ["git", "tmux", "vim", "taskwarrior"]

Within your inventory file, you can remove the host data, though you can still override the data for particular hosts. If you specify a key with host data in the inventory file, pyinfra uses this value. If not, it searches for the variable in the group data file from the host's group. If it isn't found there, it searches for the variable in a file group_data/all.py.

Installing Caddy in a Task

When your pyinfra deploy becomes more complex, it's best to break it into tasks. You can create a tasks directory and then put each task in its own Python file. I'll create a task to install the Caddy [6] web server, which will install the caddy package, upload a configuration file, and then restart and enable Caddy's systemd service. The task to accomplish this is shown in Listing 3.

Listing 3

Task to Install Caddy

01 from pyinfra import host
02 from pyinfra.operations import server, files, systemd
03
04 server.packages(name="Install Caddy", packages=["caddy"], _sudo=True)
05
06 caddyfile_installed = files.template(
07   name="Upload templated Caddyfile",
08   src="templates/Caddyfile.j2",
09   dest="/etc/caddy/Caddyfile",
10   message=host.data.welcome_message,
11   _sudo=True,
12 )
13
14 systemd.service(
15   name="Restart Caddy",
16   service="caddy",
17   restarted=True,
18   enabled=True,
19   _sudo=True,
20   _if=caddyfile_installed.did_change,
21 )

After installing Caddy, this task uses the files.template operation to generate a Jinja2 [7] template and copy the result to the Caddyfile. Then the systemd.service operation restarts Caddy's systemd service, but only if the Caddyfile was changed.

The template, in templates/Caddyfile.j2, looks like

:80 {
  respond "{{ message }}"
}

The message placeholder is replaced by the message argument in the files.template operation, which points to host.data.welcome_message. So you need to set this host data in the inventory file:

webservers = [
  ("playground.home", {"packages": ["tmux"], "welcome_message": "Hello world!"})
]

If your intent is to execute the Caddy task only on web servers, change your deploy.py according to Listing 4.

Listing 4

Import Specific Group Tasks in deploy.py

01 from pyinfra import host, local
02 from pyinfra.operations import server
03
04 server.packages(
05   name="Install packages", packages=host.data.packages, _sudo=True
06 )
07
08 if "webservers" in host.groups:
09   local.include("tasks/caddy.py")

Running tasks for specific hosts is also possible:

if host.name == "playground.home":
  local.include("tasks/caddy.py")

Buy this article as PDF

Download Article PDF now with Express Checkout
Price $2.95
(incl. VAT)

Buy Linux Magazine

Related content

  • Caddy

    Caddy lets even the most inexperienced user set up a secure web server.

  • Push Notifications

    If you host a lot of services on your home server, it helps to have them send you timely notifications. With ntfy, you can send push notifications to your phone or desktop.

  • Argos Translate and LibreTranslate

    Run your own machine translation service with Argos Translate and LibreTranslate.

  • Readeck

    Don't let interesting articles and blog posts linger in your browser's bookmarking purgatory. Use Readeck to read and manage them instead.

  • Ghost

    Ghost is a powerful CMS for beginners and professionals who want to grow a business around their content.

comments powered by Disqus
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Subscribe to our ADMIN Newsletters

Support Our Work

Linux Magazine content is made possible with support from readers like you. Please consider contributing when you’ve found an article to be beneficial.

Learn More

News