State requisites and declarations

The Salt requisite system is used to create relationships between states. The core idea being that, when one state is dependent somehow on another, that inter-dependency can be easily defined.

Requisites come in two types: Direct requisites (such as require), and requisite_ins (such as require_in). The relationships are directional: a direct requisite requires something from another state. However, a requisite_in inserts a requisite into the targeted state pointing to the targeting state. The following example demonstrates a direct requisite:

vim:
  pkg.installed: []

/etc/vimrc:
  file.managed:
    - source: salt://edit/vimrc
    - require:
      - pkg: vim

There are several direct requisite statements that can be used in Salt:

require

The use of require demands that the dependent state executes before the depending state.

watch

The watch statements are used to add additional behavior when there are changes in other states.

prereq

A prereq allows for actions to be taken based on the expected results of a state that has not yet been executed. The state containing the prereq requisite is defined as the pre-requiring state. The state specified in the prereq statement is defined as the pre-required state.

use

The use requisite is used to inherit the arguments passed in another id declaration. This is useful when many files need to have the same defaults.

The use statement was developed primarily for the networking states but can be used on any states in Salt. This made sense for the networking state because it can define a long list of options that need to be applied to multiple network interfaces.

onchanges

onfail

The requisite “_in” declarations

Since one of the most common things to do when extending another SLS is to add states for a service to watch, or anything for a watcher to watch, the requisite in statement was added to 0.9.8 to make extending the watch and require lists easier. The ssh-server extend statement above could be more cleanly defined like so:

include:
  - ssh

/etc/ssh/banner:
  file.managed:
    - source: salt://ssh/banner
    - watch_in:
      - service: ssh-server

Ordering states

Before using the order option, remember that the majority of state ordering should be done with a Requisite declaration, and that a requisite declaration will override an order option, so a state with order option should not require or required by other states.

The order option

The order option is used by adding an order number to a state declaration with the option order:

vim:
  pkg.installed:
    - order: 1

By adding the order option to 1 this ensures that the vim package will be installed in tandem with any other state declaration set to the order 1. Any state declared without an order option will be executed after all states with order options are executed.

But this construct can only handle ordering states from the beginning. Certain circumstances will present a situation where it is desirable to send a state to the end of the line. To do this, set the order to last:

vim:
  pkg.installed:
    - order: last

Including other SLS Files

When other SLS files are included, everything defined in the included SLS file will be added to the state run. When including define a list of SLS formulas to include:

include:
  - http
  - libvirt

The include statement will include SLS formulas from the same environment that the including SLS formula is in. But the environment can be explicitly defined in the configuration to override the running environment, therefore if an SLS formula needs to be included from an external environment named “dev” the following syntax is used:

include:
  - dev: http

Note

include does not simply inject the states where you place it in the SLS file. If you need to guarantee order of execution, consider using requisites.

Relative includes

You can include SLS formulas which are relative to the running SLS formula was added. Simply precede the formula name with a .:

include:
  - .virt
  - .virt.hyper

The following SLS configuration, if placed within example.dev.virtual, would result in example.http and base being included respectively:

include:
  - ..http
  - ...base

Excluding resources

The exclude statement allows an SLS to hard exclude another SLS file or a specific id. The component is excluded after the high data has been compiled, so nothing should be able to override an exclude.

Since the exclude can remove an id or an SLS the type of component to exclude needs to be defined. An exclude statement that verifies that the running highstate does not contain the http SLS and the /etc/vimrc id would look like this:

exclude:
  - sls: http
  - id: /etc/vimrc

Extending external SLS data

Sometimes a state defined in one SLS file will need to be modified from a separate SLS file. A good example of this is when an argument needs to be overwritten or when a service needs to watch an additional state.

The standard way to extend is via the extend declaration. The extend declaration is a top level declaration like include and encapsulates ID declaration data included from other SLS files. This means that extend can only be called once in an sls, if if is used twice then only one of the extend blocks will be read. A standard extend looks like this:

include:
  - http
  - ssh

extend:
  apache:
    file:
      - name: /etc/httpd/conf/httpd.conf
      - source: salt://http/httpd2.conf
  ssh-server:
    service:
      - watch:
        - file: /etc/ssh/banner

/etc/ssh/banner:
  file.managed:
    - source: salt://ssh/banner

There are a few rules to remember when extending states:

  • Always include the SLS being extended with an include declaration
  • Requisites (watch and require) are appended to, everything else is overwritten
  • extend is a top level declaration, like an ID declaration, cannot be declared twice in a single SLS
  • Many IDs can be extended under the extend declaration

Altering states

The state altering system is used to make sure that states are evaluated exactly as the user expects. It can be used to double check that a state preformed exactly how it was expected to, or to make 100% sure that a state only runs under certain conditions. The use of unless or onlyif options help make states even more stateful. The check_cmds option helps ensure that the result of a state is evaluated correctly.

unless

vim:
  pkg.installed:
    - unless:
      - rpm -q vim-enhanced
      - ls /usr/bin/vim

The unless requisite specifies that a state should only run when any of the specified commands return False. The unless requisite operates as NAND and is useful in giving more granular control over when a state should execute.

In the example above, the state will only run if either the vim-enhanced package is not installed (returns False) or if /usr/bin/vim does not exist (returns False). The state will run if both commands return False.

onlyif

The onlyif requisite specifies that if each command listed in onlyif returns True, then the state is run. If any of the specified commands return False, the state will not run.

stop-volume:
  module.run:
    - name: glusterfs.stop_volume
    - m_name: work
    - onlyif:
      - gluster volume status work
    - order: 1

remove-volume:
  module.run:
    - name: glusterfs.delete
    - m_name: work
    - onlyif:
      - gluster volume info work
    - watch:
      - cmd: stop-volume

listen/listen_in

Listen and its counterpart listen_in trigger mod_wait functions for states, when those states succeed and result in changes, similar to how watch its counterpart watch_in. Unlike watch and watch_in, listen, and listen_in will not modify the order of states and can be used to ensure your states are executed in the order they are defined. All listen/listen_in actions will occur at the end of a state run, after all states have completed.

restart-apache2:
  service.running:
    - name: apache2
    - listen:
      - file: /etc/apache2/apache2.conf

configure-apache2:
  file.managed:
    - name: /etc/apache2/apache2.conf
    - source: salt://apache2/apache2.conf

check_cmd

Check Command is used for determining that a state did or did not run as expected.

comment-repo:
  file.replace:
    - name: /etc/yum.repos.d/fedora.repo
    - pattern: ^enabled=0
    - repl: enabled=1
    - check_cmd:
      - grep 'enabled=0' /etc/yum.repos.d/fedora.repo && return 1 || return 0

State providers

Salt predetermines what modules should be mapped to what uses based on the properties of a system. These determinations are generally made for modules that provide things like package and service management.

Sometimes in states, it may be necessary to use an alternative module to provide the needed functionality. For instance, an older Arch Linux system may not be running systemd, so instead of using the systemd service module, you can revert to the default service module:

httpd:
  service.running:
    - enable: True
    - provider: service

In this instance, the basic service module (which manages sysvinit-based services) will replace the systemd module which is used by default on Debian Linux.

Lab: Add more SLS files

apache:
  pkg.installed:
    - name: {{ pillar.apache.server }}
  service.running:
    - name: {{ pillar.apache.service }}
    - enable: True

# The following states are inert by default and can be used by other states to
# trigger a restart or reload as needed.
apache-reload:
  module.wait:
    - name: service.reload
    - m_name: {{ pillar.apache.service }}

apache-restart:
  module.wait:
    - name: service.restart
    - m_name: {{ pillar.apache.service }}

Lab: Add manual ordering

Create file /srv/salt/files/extra.sls on cfg01 node.

htop:
  pkg.installed:
    - order: last

iotop:
  pkg.installed

mc:
  pkg.installed

Add reference to the file to top.sls file on cfg01 node.

Now run the highstate on the svc01.

Lab: Work with requisite declarations

Create file /srv/salt/files/webserver_extra.sls on cfg01 node.

mod_wsgi:
  pkg.installed:
    - name: {{ pillar.apache.mod_wsgi }}
    - require:
      - pkg: apache2
    - watch_in:
      - service: apache2

Add reference to the file to top.sls file.

Now run the highstate on the svc01 node.