Salt Package-File-Service with Pyobjects

When you install Apache, the most popular web server in the world, you want to show web pages. In practice, you install the package, keep the service running and modify a configuration file. In configuration management, this is called package-file-service pattern.
Salt now includes PyObjects, a pythonic interface to state descriptions. This article shows you how to implement package-file-service pattern with Salt PyObjects.

Prequisites

To follow this article, you should be fluent with Linux command line interface and installing daemons. Knowing Filesystem Hierarchy Standard is a bonus.
You should have a modern (at least 2015.8 beryllium) version of Salt Stack installed.

Create SLS State File

In configuration management, you want to describe the wanted state of your system. You can keep running your configuration management system as many times as you wish, and changes are only done if needed. This is called idempotent configuration. In Salt, idempotent configuration is described in SLS statefiles, such as apache2.sls.
We’ll be using default locations for files, even though one could easily come up with other places for these files.

$ sudo mkdir -p /srv/salt/
$ sudoedit /srv/salt/apache2.sls

The new file apache2.sls will describe apache2 installation.

#!pyobjects
with Pkg.installed("apache2"):
	File.managed("/var/www/html/index.html", contents="Welcome")
	Service.running("apache2", enable=True)
	with Service("apache2", "watch_in"):
		File.symlink("/etc/apache2/mods-enabled/userdir.conf",
			target="/etc/apache2/mods-available/userdir.conf")
		File.symlink("/etc/apache2/mods-enabled/userdir.load",
			target="/etc/apache2/mods-available/userdir.load")

Then, you can run SLS state with

$ sudo salt-call --state-output=mixed --local state.sls apache2

To test your new web server, you can use Firefox or ‘w3m -dump localhost’. You should see the plain text contents “Welcome”.
Congratulations, you just ran a salt state!

How the PyObjects “with” Works

Salt states are run in deterministic order, top down. But by default, all lines are evaluated even if the first line fails.
To make a dependency, so that the other states are evaluated only if Pkg.installed(“apache2”) succeeds, we create a dependency. By using Python “with”, everything in the block below requires Pkg(“apache2”). In this case, the indentation shows that all lines after that one require Pkg(“apache2”).

Restart the Daemon if Config Files Change

When you have modified configuration files, you must restart the daemon for changes to take effect.
You must both declare the service and then set the watch

Service.running("apache2", enable=True)
with Service("apache2", "watch_in"):
	# any change in this block restarts apache2

Package-File-Service Everywhere

Package-File-Service is the most common pattern in any configuration management system. Now that you know how it works, you’re ready to install MySQL, SSH or any other daemon your system requires.

Posted in Uncategorized | Tagged , , , , , , , , | 7 Comments

7 Responses to Salt Package-File-Service with Pyobjects

  1. For more silent output:
    $ sudo salt-call –local –state-output=terse –log-level=critical state.sls apache2


  2. tee@taulu:/srv/salt/sshd$ cat init.sls
    #!pyobjects
    Pkg.installed("openssh-server")
    Service.running("ssh", enable=True)
    with Service("ssh", "watch_in"):
    File.managed("/etc/ssh/sshd_config",
    source="salt://sshd/sshd_config.jinja",
    template="jinja",
    context={"sshport": pillar("sshport")})
    tee@taulu:/srv/salt/sshd$ grep ^Port sshd_config.jinja
    Port {{ sshport }}
    tee@taulu:/srv/salt/sshd$ head -100 /srv/pillar/*
    ==> /srv/pillar/ssh.sls /srv/pillar/top.sls <==
    base:
    '*':
    - ssh
    $ sudo salt-call --local state.sls sshd

  3. A state for SSH package file service. It’s not yet useful – the service (daemon) is not restarted when config files change.

    $ pwd
    /srv/salt
    $ ls
    sshd_config sshd.sls
    $ cat sshd.sls
    #!pyobjects
    Pkg.installed("openssh-server")
    Service.running("ssh", enable=True)
    File.managed("/etc/ssh/sshd_config", source="salt://sshd_config")
    $ grep Port sshd_config
    Port 8888
    $ sudo salt-call --local state.sls sshd

  4. Whole state for SSHd.

    $ tree /srv/salt/
    /srv/salt/
    ├── sshd_config
    └── sshd.sls
    0 directories, 2 files
    $ cat sshd.sls
    #!pyobjects
    Pkg.installed("openssh-server")
    Service.running("ssh", enable=True)
    with Service("ssh", "watch_in"):
    File.managed("/etc/ssh/sshd_config", source="salt://sshd_config")
    $ grep Port sshd_config
    Port 8888
    $ sudo salt-call --local state.sls sshd -l debug --state-output=mixed


  5. $ cat /srv/pillar/top.sls
    base:
    '*':
    - ssh
    $ cat /srv/pillar/ssh.sls
    ssh:
    port: 1917
    $ cat /srv/salt/sshd.sls
    #!pyobjects
    Pkg.installed("openssh-server")
    Service.running("ssh", enable=True)
    with Service("ssh", "watch_in"):
    File.managed("/etc/ssh/sshd_config",
    source="salt://sshd_config.jinja",
    template="jinja",
    context={"ssh": pillar("ssh")} )
    $ cat /srv/salt/sshd_config.jinja |grep Port
    Port {{ ssh.port }}

  6. Tatu says:

    Miten ihan perus ssh-asennus Saltilla tehtiin? Jostain syystä en saa sitä nyt onnistumaan…

  7. Tatu says:

    Onnistuin jo.