Implementing Package-File-Service Pattern as a Salt Stack PyObjects Function

PyObjects renderer allows you to use normal Python functions to repeat actions. In this article, we’ll implement a Package-File-Service pattern as a reusable Python function.
To follow this article, you must be fluent with Linux command line interface, know Salt Stack, PyObjects renderer and Python programming language. The code has been written on Salt Beryllium.
This article is beta quality, as I had to change some strings to different from the system I used this on. The code itself is alpha, don’t go deploying it on your production servers yet.

The End Result

You don’t need many files:

$ find /srv/salt/dhcpd/
dhcpd.sls   # your module
pfs.sls     # pfs.sls library, download it from this page
dhcpd.conf.jinja   # your template

Your module only needs 3 lines (yes, three) to implement package-file-service.

from salt://manage/pxemaster/pfs.sls import pfs
pfs("isc-dhcp-server", files="/etc/dhcp/dhcpd.conf", module="dhcpd")

You can run it normally.

$ sudo salt-call --local state.sls manage/pxemaster/ -l debug --state-output=mixed

It’s a real package-file-service pattern, so that the daemon is reloaded automatically as needed.

Advanced Usage

This is the library that implements the pfs(service, file) pattern.
There are many ways to call pfs(). Short alias name with a single file:

pfs("isc-dhcp-server", files="/etc/dhcp/dhcpd.conf", module="dhcpd"))

You must provide the template file dhcpd.conf.jinja in the same directory. Template name is the file basename (path removed) and suffix “.jinja” added. You are free to use jinja markup or leave it out.
Long function name (exactly the same function as pfs()):

packageFileService("isc-dhcp-server", files="/etc/dhcp/dhcpd.conf", module="dhcpd")

Multiple configuration files. Notice how files parameter must be list when there are multiple files:

pfs("isc-dhcp-server", files=["/etc/dhcp/dhcpd.conf", "/tmp/foobar"], module="dhcpd")

If your service (daemon) has different name from the package, use service parameter

pfs("foo-utils", "/etc/food", service="food", , module="dhcpd")

Currently, you pfs.sls doesn’t automatically detect your module (the directory under /srv/salt/ containing your init.sls). Instead, you can provide it with the module parameter.

Download pfs.sls

You can copy pfs.sls from below. The pfs.sls code licenced under GPL 3 or later.

# Copyright 2015 Tero Karvinen
def packageFileService(package, files, service=None, module=None, context=None):
        "Package-file-service pattern as function. "
        from os.path import basename    # import must be inside the function declaration
        assert type(package)==str
        if type(files)==str:
        assert type(files)==list
        if service==None:
        assert len(module)>0    # autodetect not yet implemented
        with Pkg.installed(package):
                Service.running(service, enable=True)
                with Service(service, "watch_in"):
                        # TODO: automatic relative source salt:// path, see config("arg") and others
                        for target in files:
                                source="salt://%s/%s.jinja" % (module, basename(target))
                                File.managed(target, source=source, template="jinja",
pfs = packageFileService
Posted in Uncategorized | Tagged , , , , , , , , | Comments Off on Implementing Package-File-Service Pattern as a Salt Stack PyObjects Function

Comments are closed.