neulab/tldr huggingface dataset:
Additionally, the Python seco range libraries must be installed on the salt master. One can verify that they have been installed correctly via the following command:
python -c 'import seco.range'
If no errors are returned, range is installed successfully on the salt master.
Preparing Salt Range support must be enabled on the salt master by setting the hostname and port of the range server inside the master configuration file:
range_server: my.range.server.com:80
Following this, the master must be restarted for the change to have an effect.
Targeting with Range Once a cluster has been defined, it can be targeted with a salt command by using the -R or --range flags.
For example, given the following range YAML file being served from a range server:
$ cat /etc/range/test.yaml CLUSTER: host1..100.test.com APPS: - frontend - backend - mysql
One might target host1 through host100 in the test.com domain with Salt as follows:
salt --range %test:CLUSTER test.version
The following salt command would target three hosts: frontend, backend, and mysql:
salt --range %test:APPS test.version
Loadable Matchers New in version 2019.2.0.
Internally targeting is implemented with chunks of code called Matchers. As of the 2019.2.0 release, matchers can be loaded dynamically. Currently new matchers cannot be created, but existing matchers can have their functionality altered or extended.  For more information on Matchers see
Matchers New in version 3000.
Matchers are modules that provide Salt's targeting abilities.  As of the 3000 release, matchers can be dynamically loaded.  Currently new matchers cannot be created because the required plumbing for the CLI does not exist yet.  Existing matchers may have their functionality altered or extended.
For details of targeting methods, see the Targeting topic.
A matcher module must have a function called match(). This function ends up becoming a method on the Matcher class.  All matcher functions require at least two arguments, self (because the function will be turned into a method), and tgt, which is the actual target string.  The grains and pillar matchers also take a delimiter argument and should default to DEFAULT_TARGET_DELIM.
Like other Salt loadable modules, modules that override built-in functionality can be placed in file_roots in a special directory and then copied to the minion through the normal sync process.  saltutil.sync_all will transfer all loadable modules, and the 3000 release introduces saltutil.sync_matchers.  For matchers, the directory is /srv/salt/_matchers (assuming your file_roots is set to the default /srv/salt).
As an example, let's modify the list matcher to have the separator be a '/' instead of the default ','.
from __future__ import absolute_import, print_function, unicode_literals from salt.ext import six  # pylint: disable=3rd-party-module-not-gated
def match(self, tgt): """ Determines if this host is on the list """ if isinstance(tgt, six.string_types): # The stock matcher splits on `,`.  Change to `/` below. tgt = tgt.split("/") return bool(self.opts["id"] in tgt)
Place this code in a file called list_matcher.py in _matchers in your file_roots. Sync this down to your minions with saltutil.sync_matchers. Then attempt to match with the following, replacing minionX with three of your minions.
salt -L 'minion1/minion2/minion3' test.ping
Three of your minions should respond.
The current supported matchers and associated filenames are
┌────────────────┬─────────────────────┬─────────────────────┐ │Salt CLI Switch │ Match Type          │ Filename            │ ├────────────────┼─────────────────────┼─────────────────────┤ │<none>          │ Glob                │ glob_match.py       │ ├────────────────┼─────────────────────┼─────────────────────┤ │-C              │ Compound            │ compound_match.py   │ ├────────────────┼─────────────────────┼─────────────────────┤ │-E              │ Perl-Compatible     │ pcre_match.py       │ │                │ Regular Expressions │                     │ ├────────────────┼─────────────────────┼─────────────────────┤ │-L              │ List                │ list_match.py       │ ├────────────────┼─────────────────────┼─────────────────────┤ │-G              │ Grain               │ grain_match.py      │ ├────────────────┼─────────────────────┼─────────────────────┤ │-P              │ Grain               │ grain_pcre_match.py │ │                │ Perl-Compatible     │                     │ │                │ Regular Expressions │                     │ ├────────────────┼─────────────────────┼─────────────────────┤ │-N              │ Nodegroup           │ nodegroup_match.py  │ ├────────────────┼─────────────────────┼─────────────────────┤ │-R              │ Range               │ range_match.py      │ ├────────────────┼─────────────────────┼─────────────────────┤ │-I              │ Pillar              │ pillar_match.py     │ ├────────────────┼─────────────────────┼─────────────────────┤ │-J              │ Pillar              │ pillar_pcre.py      │ │                │ Perl-Compatible     │                     │ │                │ Regular Expressions │                     │ ├────────────────┼─────────────────────┼─────────────────────┤ │-S              │ IP-Classless        │ ipcidr_match.py     │ │                │ Internet Domain     │                     │ │                │ Routing             │                     │ └────────────────┴─────────────────────┴─────────────────────┘
The Salt Mine The Salt Mine is used to collect arbitrary data from Minions and store it on the Master. This data is then made available to all Minions via the salt.modules.mine module.
Mine data is gathered on the Minion and sent back to the Master where only the most recent data is maintained (if long term data is required use returners or the external job cache).
Mine vs Grains Mine data is designed to be much more up-to-date than grain data. Grains are refreshed on a very limited basis and are largely static data. Mines are designed to replace slow peer publishing calls when Minions need data from other Minions. Rather than having a Minion reach out to all the other Minions for a piece of data, the Salt Mine, running on the Master, can collect it from all the Minions every Mine Interval, resulting in almost fresh data at any given time, with much less overhead.
Mine Functions To enable the Salt Mine the mine_functions option needs to be applied to a Minion. This option can be applied via the Minion's configuration file, or the Minion's Pillar. The mine_functions option dictates what functions are being executed and allows for arguments to be passed in.  The list of functions are available in the salt.module.  If no arguments are passed, an empty list must be added like in the test.ping function in the example below:
mine_functions: test.ping: [] network.ip_addrs: interface: eth0 cidr: 10.0.0.0/8
In the example above salt.modules.network.ip_addrs has additional filters to help narrow down the results.  In the above example IP addresses are only returned if they are on a eth0 interface and in the 10.0.0.0/8 IP range.
Changed in version 3000.
The format to define mine_functions has been changed to allow the same format as used for module.run. The old format (above) will still be supported.
mine_functions: test.ping: [] network.ip_addrs: - interface: eth0 - cidr: 10.0.0.0/8 test.arg: - isn't - this - fun - this: that - salt: stack
Minion-side Access Control New in version 3000.
Mine functions can be targeted to only be available to specific minions. This uses the same targeting parameters as targeting but with keywords allow_tgt and allow_tgt_type. When a minion requests a function from the salt mine that is not allowed to be requested by that minion (i.e. when looking up the combination of allow_tgt and allow_tgt_type and the requesting minion is not in the list) it will get no data, just as if the requested function is not present in the salt mine.
mine_functions: network.ip_addrs: - interface: eth0 - cidr: 10.0.0.0/8 - allow_tgt: 'G@role:master' - allow_tgt_type: 'compound'
Mine Functions Aliases Function aliases can be used to provide friendly names, usage intentions or to allow multiple calls of the same function with different arguments. There is a different syntax for passing positional and key-value arguments. Mixing positional and key-value arguments is not supported.
mine_functions: network.ip_addrs: [eth0] networkplus.internal_ip_addrs: [] internal_ip_addrs: mine_function: network.ip_addrs cidr: 192.168.0.0/16 ip_list: - mine_function: grains.get - ip_interfaces
With the addition of the module.run-like format for defining mine_functions, the method of adding aliases remains similar. Just add a mine_function kwarg with the name of the real function to call, making the key below mine_functions the alias:
mine_functions: alias_name: - mine_function: network.ip_addrs - eth0 internal_ip_addrs: - mine_function: network.ip_addrs - cidr: 192.168.0.0/16 ip_list: - mine_function: grains.get - ip_interfaces
Mine Interval The Salt Mine functions are executed when the Minion starts and at a given interval by the scheduler. The default interval is every 60 minutes and can be adjusted for the Minion via the mine_interval option in the minion config:
Mine in Salt-SSH As of the 2015.5.0 release of salt, salt-ssh supports mine.get.
Because the Minions cannot provide their own mine_functions configuration, we retrieve the args for specified mine functions in one of three places, searched in the following order:
1. Roster data
2. Pillar
3. Master config
The mine_functions are formatted exactly the same as in normal salt, just stored in a different location. Here is an example of a flat roster containing mine_functions:
test: host: 104.237.131.248 user: root mine_functions: cmd.run: ['echo "hello!"'] network.ip_addrs: interface: eth0
NOTE: Because of the differences in the architecture of salt-ssh, mine.get calls are somewhat inefficient. Salt must make a new salt-ssh call to each of the Minions in question to retrieve the requested data, much like a publish call. However, unlike publish, it must run the requested function as a wrapper function, so we can retrieve the function args from the pillar of the Minion in question. This results in a non-trivial delay in retrieving the requested data.
Minions Targeting with Mine The mine.get function supports various methods of Minions targeting to fetch Mine data from particular hosts, such as glob or regular expression matching on Minion id (name), grains, pillars and compound matches. See the salt.modules.mine module documentation for the reference.
NOTE: Pillar data needs to be cached on Master for pillar targeting to work with Mine. Read the note in relevant section.
Example One way to use data from Salt Mine is in a State. The values can be retrieved via Jinja and used in the SLS file. The following example is a partial HAProxy configuration file and pulls IP addresses from all Minions with the "web" grain to add them to the pool of load balanced servers.
base: 'G@roles:web': - web
/srv/pillar/web.sls:
mine_functions: network.ip_addrs: [eth0]
Then trigger the minions to refresh their pillar data by running:
Verify that the results are showing up in the pillar on the minions by executing the following and checking for network.ip_addrs in the output:
Which should show that the function is present on the minion, but not include the output:
minion1.example.com: ---------- mine_functions: ---------- network.ip_addrs: - eth0
Mine data is typically only updated on the master every 60 minutes, this can be modified by setting:
/etc/salt/minion.d/mine.conf:
mine_interval: 5
To force the mine data to update immediately run:
salt '*' mine.update
Setup the salt.states.file.managed state in /srv/salt/haproxy.sls:
haproxy_config: file.managed: - name: /etc/haproxy/config - source: salt://haproxy_config - template: jinja
Create the Jinja template in /srv/salt/haproxy_config:
<...file contents snipped...>
{% for server, addrs in salt['mine.get']('roles:web', 'network.ip_addrs', tgt_type='grain') | dictsort() %} server {{ server }} {{ addrs[0] }}:80 check {% endfor %}
In the above example, server will be expanded to the minion_id.
NOTE: The expr_form argument will be renamed to tgt_type in the 2017.7.0 release of Salt.
Runners Salt runners are convenience applications executed with the salt-run command.
Salt runners work similarly to Salt execution modules however they execute on the Salt master itself instead of remote Salt minions.
A Salt runner can be a simple client call or a complex application.
SEE ALSO: The full list of runners
Writing Salt Runners A Salt runner is written in a similar manner to a Salt execution module. Both are Python modules which contain functions and each public function is a runner which may be executed via the salt-run command.
For example, if a Python module named test.py is created in the runners directory and contains a function called foo, the test runner could be invoked with the following command:
# salt-run test.foo
Runners have several options for controlling output.
Any print statement in a runner is automatically also fired onto the master event bus where. For example:
def a_runner(outputter=None, display_progress=False): print("Hello world") ...
The above would result in an event fired as follows:
Event fired at Tue Jan 13 15:26:45 2015 ************************* Tag: salt/run/20150113152644070246/print Data: {'_stamp': '2015-01-13T15:26:45.078707', 'data': 'hello', 'outputter': 'pprint'}
A runner may also send a progress event, which is displayed to the user during runner execution and is also passed across the event bus if the display_progress argument to a runner is set to True.
A custom runner may send its own progress event by using the __jid_event_.fire_event() method as shown here:
if display_progress: __jid_event__.fire_event({"message": "A progress message"}, "progress")
The above would produce output on the console reading: A progress message as well as an event on the event similar to:
Event fired at Tue Jan 13 15:21:20 2015 ************************* Tag: salt/run/20150113152118341421/progress Data: {'_stamp': '2015-01-13T15:21:20.390053', 'message': "A progress message"}
A runner could use the same approach to send an event with a customized tag onto the event bus by replacing the second argument (progress) with whatever tag is desired. However, this will not be shown on the command-line and will only be fired onto the event bus.
Synchronous vs. Asynchronous A runner may be fired asynchronously which will immediately return control. In this case, no output will be display to the user if salt-run is being used from the command-line. If used programmatically, no results will be returned.  If results are desired, they must be gathered either by firing events on the bus from the runner and then watching for them or by some other means.
NOTE: When running a runner in asynchronous mode, the --progress flag will not deliver output to the salt-run CLI. However, progress events will still be fired on the bus.
In synchronous mode, which is the default, control will not be returned until the runner has finished executing.
To add custom runners, put them in a directory and add it to runner_dirs in the master configuration file.
Examples Examples of runners can be found in the Salt distribution:
https://github.com/saltstack/salt/blob/master/salt/runners
A simple runner that returns a well-formatted list of the minions that are responding to Salt calls could look like this:
# Import salt modules import salt.client
def up(): """ Print a list of all of the minions that are up """ client = salt.client.LocalClient(__opts__["conf_file"]) minions = client.cmd("*", "test.version", timeout=1) for minion in sorted(minions): print(minion)
Salt Engines New in version 2015.8.0.
Salt Engines are long-running, external system processes that leverage Salt.
• Engines have access to Salt configuration, execution modules, and runners (__opts__, __salt__, and __runners__).
• Engines are executed in a separate process that is monitored by Salt. If a Salt engine stops, it is restarted automatically.
• Engines can run on the Salt master and on Salt minions.
Salt engines enhance and replace the external processes functionality.
Configuration Salt engines are configured under an engines top-level section in your Salt master or Salt minion configuration. Provide a list of engines and parameters under this section.
engines: - logstash: host: log.my_network.com port: 5959 proto: tcp
Multiple copies of a particular Salt engine can be configured by including the engine_module parameter in the engine configuration.
engines: - production_logstash: host: production_log.my_network.com port: 5959 proto: tcp engine_module: logstash - develop_logstash: host: develop_log.my_network.com port: 5959 proto: tcp engine_module: logstash
Salt engines must be in the Salt path, or you can add the engines_dirs option in your Salt master configuration with a list of directories under which Salt attempts to find Salt engines. This option should be formatted as a list of directories to search, such as:
engines_dirs: - /home/bob/engines
Writing an Engine An example Salt engine, https://github.com/saltstack/salt/blob/master/salt/engines/test.py, is available in the Salt source. To develop an engine, the only requirement is that your module implement the start() function.
What is YAML and How To Use It The default renderer for SLS files is the YAML renderer.
What is YAML What does YAML stand for? It's an acronym for YAML Ain't Markup Language.
The Official YAML Website defines YAML as: ...a human friendly data serialization standard for all programming languages.
However, Salt uses a small subset of YAML that maps over very commonly used data structures, like lists and dictionaries. It is the job of the YAML renderer to take the YAML data structure and compile it into a Python data structure for use by Salt.
Defining YAML Though YAML syntax may seem daunting and terse at first, there are only three very simple rules to remember when writing YAML for SLS files.
Dictionary keys are represented in YAML as strings terminated by a trailing colon. Values are represented by either a string following the colon, separated by a space:
Alternatively, a value can be associated with a key through indentation.
NOTE: The above syntax is valid YAML but is uncommon in SLS files because most often, the value for a key is not singular but instead is a list of values.
{"my_dictionary": ["list_value_one", "list_value_two", "list_value_three"]}
Learning more about YAML One easy way to learn more about how YAML gets rendered into Python data structures is to use an online YAML parser to see the Python output.
Here are some excellent links for experimenting with and referencing YAML:
• Online YAML Parser: Convert YAML to JSON or Python data structures.
• The Official YAML Specification
• The Wikipedia page for YAML
Templating Jinja statements and expressions are allowed by default in SLS files. See Understanding Jinja.
Understanding Jinja Jinja is the default templating language in SLS files.
Jinja in States Jinja is evaluated before YAML, which means it is evaluated before the States are run.
The most basic usage of Jinja in state files is using control structures to wrap conditional or redundant state elements:
{% if grains['os'] != 'FreeBSD' %} tcsh: pkg: - installed {% endif %}
motd: file.managed: {% if grains['os'] == 'FreeBSD' %} - name: /etc/motd {% elif grains['os'] == 'Debian' %} - name: /etc/motd.tail {% endif %} - source: salt://motd
In this example, the first if block will only be evaluated on minions that aren't running FreeBSD, and the second block changes the file name based on the os grain.
Writing if-else blocks can lead to very redundant state files however. In this case, using pillars, or using a previously defined variable might be easier:
{% set motd = ['/etc/motd'] %} {% if grains['os'] == 'Debian' %} {% set motd = ['/etc/motd.tail', '/var/run/motd'] %} {% endif %}
{% for motdfile in motd %} {{ motdfile }}: file.managed: - source: salt://motd {% endfor %}
Using a variable set by the template, the for loop will iterate over the list of MOTD files to update, adding a state block for each file.
The filter_by function can also be used to set variables based on grains:
{% set auditd = salt['grains.filter_by']({ 'RedHat': { 'package': 'audit' }, 'Debian': { 'package': 'auditd' }, }) %}
Include and Import Includes and imports can be used to share common, reusable state configuration between state files and between files.
{% from 'lib.sls' import test %}
This would import the test template variable or macro, not the test state element, from the file lib.sls. In the case that the included file performs checks against grains, or something else that requires context, passing the context into the included file is required:
{% from 'lib.sls' import test with context %}
Includes must use full paths, like so:
spam/eggs.jinja
{% include 'spam/foobar.jinja' %}
Including Context During Include/Import By adding with context to the include/import directive, the current context can be passed to an included/imported template.
{% import 'openssl/vars.sls' as ssl with context %}
Macros Macros are helpful for eliminating redundant code. Macros are most useful as mini-templates to repeat blocks of strings with a few parameterized variables.  Be aware that stripping whitespace from the template block, as well as contained blocks, may be necessary to emulate a variable return from the macro.
# init.sls {% from 'lib.sls' import pythonpkg with context %}
python-virtualenv: pkg.installed: - name: {{ pythonpkg('virtualenv') }}
python-fabric: pkg.installed: - name: {{ pythonpkg('fabric') }}
# lib.sls {% macro pythonpkg(pkg) -%} {%- if grains['os'] == 'FreeBSD' -%} py27-{{ pkg }} {%- elif grains['os'] == 'Debian' -%} python-{{ pkg }} {%- endif -%} {%- endmacro %}
This would define a macro that would return a string of the full package name, depending on the packaging system's naming convention. The whitespace of the macro was eliminated, so that the macro would return a string without line breaks, using whitespace control.
Template Inheritance Template inheritance works fine from state files and files. The search path starts at the root of the state tree or pillar.
Errors Saltstack allows raising custom errors using the raise jinja function.
{{ raise('Custom Error') }}
When rendering the template containing the above statement, a TemplateError exception is raised, causing the rendering to fail with the following message:
TemplateError: Custom Error
Filters Saltstack extends builtin filters with these custom filters:
strftime Converts any time related object into a time based string. It requires valid strftime directives. An exhaustive list can be found here in the Python documentation.
{% set curtime = None | strftime() %}
Fuzzy dates require the timelib Python module is installed.
{{ "2002/12/25"|strftime("%y") }} {{ "1040814000"|strftime("%Y-%m-%d") }} {{ datetime|strftime("%u") }} {{ "tomorrow"|strftime }}
sequence Ensure that parsed data is a sequence.
yaml_encode Serializes a single object into a YAML scalar with any necessary handling for escaping special characters.  This will work for any scalar YAML data type: ints, floats, timestamps, booleans, strings, unicode.  It will not work for multi-objects such as sequences or maps.
{%- set bar = 7 %} {%- set baz = none %} {%- set zip = true %} {%- set zap = 'The word of the day is "salty"' %}
{%- load_yaml as foo %} bar: {{ bar|yaml_encode }} baz: {{ baz|yaml_encode }} zip: {{ zip|yaml_encode }} zap: {{ zap|yaml_encode }} {%- endload %}
In the above case {{ bar }} and {{ foo.bar }} should be identical and {{ baz }} and {{ foo.baz }} should be identical.
yaml_dquote Serializes a string into a properly-escaped YAML double-quoted string. This is useful when the contents of a string are unknown and may contain quotes or unicode that needs to be preserved.  The resulting string will be emitted with opening and closing double quotes.
{%- set bar = '"The quick brown fox . . ."' %} {%- set baz = 'The word of the day is "salty".' %}
{%- load_yaml as foo %} bar: {{ bar|yaml_dquote }} baz: {{ baz|yaml_dquote }} {%- endload %}
In the above case {{ bar }} and {{ foo.bar }} should be identical and {{ baz }} and {{ foo.baz }} should be identical.  If variable contents are not guaranteed to be a string then it is better to use yaml_encode which handles all YAML scalar types.
yaml_squote Similar to the yaml_dquote filter but with single quotes.  Note that YAML only allows special escapes inside double quotes so yaml_squote is not nearly as useful (viz. you likely want to use yaml_encode or yaml_dquote).
to_bool New in version 2017.7.0.
Returns the logical value of an element.
{{ 'yes' | to_bool }} {{ 'true' | to_bool }} {{ 1 | to_bool }} {{ 'no' | to_bool }}
Will be rendered as:
True True True False
exactly_n_true New in version 2017.7.0.
Tests that exactly N items in an iterable are "truthy" (neither None, False, nor 0).
{{ ['yes', 0, False, 'True'] | exactly_n_true(2) }}
exactly_one_true New in version 2017.7.0.
Tests that exactly one item in an iterable is "truthy" (neither None, False, nor 0).
{{ ['yes', False, 0, None] | exactly_one_true }}
quote New in version 2017.7.0.
This text will be wrapped in quotes.
regex_search New in version 2017.7.0.
Scan through string looking for a location where this regular expression produces a match. Returns None in case there were no matches found
{{ 'abcdefabcdef' | regex_search('BC(.*)', ignorecase=True) }}
("defabcdef",)
regex_match New in version 2017.7.0.
If zero or more characters at the beginning of string match this regular expression, otherwise returns None.
{{ 'abcdefabcdef' | regex_match('BC(.*)', ignorecase=True) }}
regex_replace New in version 2017.7.0.
Searches for a pattern and replaces with a sequence of characters.
{% set my_text = 'yes, this is a TEST' %} {{ my_text | regex_replace(' ([a-z])', '__\\1', ignorecase=True) }}
yes,__this__is__a__TEST
uuid New in version 2017.7.0.
Return a UUID.
{{ 'random' | uuid }}
3652b285-26ad-588e-a5dc-c2ee65edc804
is_list New in version 2017.7.0.
Return if an object is list.
{{ [1, 2, 3] | is_list }}
is_iter New in version 2017.7.0.
Return if an object is iterable.
{{ [1, 2, 3] | is_iter }}
min New in version 2017.7.0.
Return the minimum value from a list.
{{ [1, 2, 3] | min }}
max New in version 2017.7.0.
Returns the maximum value from a list.
{{ [1, 2, 3] | max }}
avg New in version 2017.7.0.
Returns the average value of the elements of a list
{{ [1, 2, 3] | avg }}
union New in version 2017.7.0.
Return the union of two lists.
{{ [1, 2, 3] | union([2, 3, 4]) | join(', ') }}
intersect New in version 2017.7.0.
Return the intersection of two lists.
{{ [1, 2, 3] | intersect([2, 3, 4]) | join(', ') }}
difference New in version 2017.7.0.
Return the difference of two lists.
{{ [1, 2, 3] | difference([2, 3, 4]) | join(', ') }}
symmetric_difference New in version 2017.7.0.
Return the symmetric difference of two lists.
{{ [1, 2, 3] | symmetric_difference([2, 3, 4]) | join(', ') }}
1, 4
method_call New in version 3001.
Returns a result of object's method call.
Example #1:
{{ [1, 2, 1, 3, 4] | method_call('index', 1, 1, 3) }}
This filter can be used with the map filter to apply object methods without using loop constructs or temporary variables.
Example #2:
{% set host_list = ['web01.example.com', 'db01.example.com'] %} {% set host_list_split = [] %} {% for item in host_list %} {% do host_list_split.append(item.split('.', 1)) %} {% endfor %} {{ host_list_split }}
Example #3:
{{ host_list|map('method_call', 'split', '.', 1)|list }}
Return of examples #2 and #3:
[[web01, example.com], [db01, example.com]]
is_sorted New in version 2017.7.0.
Return True if an iterable object is already sorted.
{{ [1, 2, 3] | is_sorted }}
compare_lists New in version 2017.7.0.
Compare two lists and return a dictionary with the changes.
{{ [1, 2, 3] | compare_lists([1, 2, 4]) }}
{"new": [4], "old": [3]}
compare_dicts New in version 2017.7.0.
Compare two dictionaries and return a dictionary with the changes.
{{ {'a': 'b'} | compare_dicts({'a': 'c'}) }}
{"a": {"new": "c", "old": "b"}}
is_hex New in version 2017.7.0.
Return True if the value is hexadecimal.
{{ '0xabcd' | is_hex }} {{ 'xyzt' | is_hex }}
True False
contains_whitespace New in version 2017.7.0.
Return True if a text contains whitespaces.
{{ 'abcd' | contains_whitespace }} {{ 'ab cd' | contains_whitespace }}
False True
substring_in_list New in version 2017.7.0.
Return True if a substring is found in a list of string values.
{{ 'abcd' | substring_in_list(['this', 'is', 'an abcd example']) }}
check_whitelist_blacklist New in version 2017.7.0.
Check a whitelist and/or blacklist to see if the value matches it.
This filter can be used with either a whitelist or a blacklist individually, or a whitelist and a blacklist can be passed simultaneously.
If whitelist is used alone, value membership is checked against the whitelist only. If the value is found, the function returns True. Otherwise, it returns False.
If blacklist is used alone, value membership is checked against the blacklist only. If the value is found, the function returns False. Otherwise, it returns True.
If both a whitelist and a blacklist are provided, value membership in the blacklist will be examined first. If the value is not found in the blacklist, then the whitelist is checked. If the value isn't found in the whitelist, the function returns False.
Whitelist Example:
{{ 5 | check_whitelist_blacklist(whitelist=[5, 6, 7]) }}
Blacklist Example:
{{ 5 | check_whitelist_blacklist(blacklist=[5, 6, 7]) }}
date_format New in version 2017.7.0.
Converts unix timestamp into human-readable string.
{{ 1457456400 | date_format }} {{ 1457456400 | date_format('%d.%m.%Y %H:%M') }}
2017-03-08 08.03.2017 17:00
to_num New in version 2017.7.0.
New in version 2018.3.0: Renamed from str_to_num to to_num.
Converts a string to its numerical value.
{{ '5' | to_num }}
to_bytes New in version 2017.7.0.
Converts string-type object to bytes.
{{ 'wall of text' | to_bytes }}
NOTE: This option may have adverse effects when using the default renderer, jinja|yaml. This is due to the fact that YAML requires proper handling in regard to special characters. Please see the section on YAML ASCII support in the YAML Idiosyncracies documentation for more information.
json_encode_list New in version 2017.7.0.
New in version 2018.3.0: Renamed from json_decode_list to json_encode_list. When you encode something you get bytes, and when you decode, you get your locale's encoding (usually a unicode type). This filter was incorrectly-named when it was added. json_decode_list will be supported until the 3003 release.
Deprecated since version 2018.3.3,2019.2.0: The tojson filter accomplishes what this filter was designed to do, making this filter redundant.
Recursively encodes all string elements of the list to bytes.
{{ [1, 2, 3] | json_encode_list }}
[1, 2, 3]
json_encode_dict New in version 2017.7.0.
New in version 2018.3.0: Renamed from json_decode_dict to json_encode_dict. When you encode something you get bytes, and when you decode, you get your locale's encoding (usually a unicode type). This filter was incorrectly-named when it was added. json_decode_dict will be supported until the 3003 release.
Recursively encodes all string items in the dictionary to bytes.
Assuming that pillar['foo'] contains {u'a': u'\u0414'}, and your locale is en_US.UTF-8:
{{ pillar['foo'] | json_encode_dict }}
{"a": "\xd0\x94"}
tojson New in version 2018.3.3,2019.2.0.
Dumps a data structure to JSON.
This filter was added to provide this functionality to hosts which have a Jinja release older than version 2.9 installed. If Jinja 2.9 or newer is installed, then the upstream version of the filter will be used. See the upstream docs for more information.
random_hash New in version 2017.7.0.
New in version 2018.3.0: Renamed from rand_str to random_hash to more accurately describe what the filter does. rand_str will be supported to ensure backwards compatibility but please use the preferred random_hash.
Generates a random number between 1 and the number passed to the filter, and then hashes it. The default hash type is the one specified by the minion's hash_type config option, but an alternate hash type can be passed to the filter as an argument.
{% set num_range = 99999999 %} {{ num_range | random_hash }} {{ num_range | random_hash('sha512') }}
43ec517d68b6edd3015b3edc9a11367b d94a45acd81f8e3107d237dbc0d5d195f6a52a0d188bc0284c0763ece1eac9f9496fb6a531a296074c87b3540398dace1222b42e150e67c9301383fde3d66ae5
set_dict_key_value New in version 3000.
Allows you to set a value in a nested dictionary without having to worry if all the nested keys actually exist.  Missing keys will be automatically created if they do not exist.  The default delimiter for the keys is ':', however, with the delimiter-parameter, a different delimiter can be specified.
Example 1: {%- set foo = {} %} {{ foo | set_dict_key_value('bar:baz', 42) }}
Example 2: {{ {} | set_dict_key_value('bar.baz.qux', 42, delimiter='.') }}
Example 1: {'bar': {'baz': 42}}
Example 2: {'bar': {'baz': {'qux': 42}}}
append_dict_key_value New in version 3000.
Allows you to append to a list nested (deep) in a dictionary without having to worry if all the nested keys (or the list itself) actually exist.  Missing keys will automatically be created if they do not exist. The default delimiter for the keys is ':', however, with the delimiter-parameter, a different delimiter can be specified.
Example 1: {%- set foo = {'bar': {'baz': [1, 2]}} %} {{ foo | append_dict_key_value('bar:baz', 42) }}
Example 2: {%- set foo = {} %} {{ foo | append_dict_key_value('bar:baz:qux', 42) }}
Example 1: {'bar': {'baz': [1, 2, 42]}}
Example 2: {'bar': {'baz': {'qux': [42]}}}
extend_dict_key_value New in version 3000.
Allows you to extend a list nested (deep) in a dictionary without having to worry if all the nested keys (or the list itself) actually exist. Missing keys will automatically be created if they do not exist.  The default delimiter for the keys is ':', however, with the delimiter-parameter, a different delimiter can be specified.
Example 1: {%- set foo = {'bar': {'baz': [1, 2]}} %} {{ foo | extend_dict_key_value('bar:baz', [42, 42]) }}
Example 2: {{ {} | extend_dict_key_value('bar:baz:qux', [42]) }}
Example 1: {'bar': {'baz': [1, 2, 42, 42]}}
update_dict_key_value New in version 3000.
Allows you to update a dictionary nested (deep) in another dictionary without having to worry if all the nested keys actually exist.  Missing keys will automatically be created if they do not exist.  The default delimiter for the keys is ':', however, with the delimiter-parameter, a different delimiter can be specified.
Example 1: {%- set foo = {'bar': {'baz': {'qux': 1}}} %} {{ foo | update_dict_key_value('bar:baz', {'quux': 3}) }}
Example 2: {{ {} | update_dict_key_value('bar:baz:qux', {'quux': 3}) }}
Example 1: {'bar': {'baz': {'qux': 1, 'quux': 3}}}
Example 2: {'bar': {'baz': {'qux': {'quux': 3}}}}
md5 New in version 2017.7.0.
Return the md5 digest of a string.
{{ 'random' | md5 }}
7ddf32e17a6ac5ce04a8ecbf782ca509
sha256 New in version 2017.7.0.
Return the sha256 digest of a string.
{{ 'random' | sha256 }}
a441b15fe9a3cf56661190a0b93b9dec7d04127288cc87250967cf3b52894d11
sha512 New in version 2017.7.0.
Return the sha512 digest of a string.
{{ 'random' | sha512 }}
811a90e1c8e86c7b4c0eef5b2c0bf0ec1b19c4b1b5a242e6455be93787cb473cb7bc9b0fdeb960d00d5c6881c2094dd63c5c900ce9057255e2a4e271fc25fef1
base64_encode New in version 2017.7.0.
Encode a string as base64.
{{ 'random' | base64_encode }}
cmFuZG9t
base64_decode New in version 2017.7.0.
Decode a base64-encoded string.
{{ 'Z2V0IHNhbHRlZA==' | base64_decode }}
get salted
hmac New in version 2017.7.0.
Verify a challenging hmac signature against a string / shared-secret. Returns a boolean value.
{{ 'get salted' | hmac('shared secret', 'eBWf9bstXg+NiP5AOwppB5HMvZiYMPzEM9W5YMm/AmQ=') }}
http_query New in version 2017.7.0.
Return the HTTP reply object from a URL.
{{ 'http://jsonplaceholder.typicode.com/posts/1' | http_query }}
{ 'body': '{ "userId": 1, "id": 1, "title": "sunt aut facere repellat provident occaecati excepturi option reprehenderit", "body": "quia et suscipit\\nsuscipit recusandae consequuntur expedita et cum\\nreprehenderit molestiae ut ut quas totam\\nnostrum rerum est autem sunt rem eveniet architecto" }' }
traverse New in version 2018.3.3.
Traverse a dict or list using a colon-delimited target string.  The target 'foo:bar:0' will return data['foo']['bar'][0] if this value exists, and will otherwise return the provided default value.
{{ {'a1': {'b1': {'c1': 'foo'}}, 'a2': 'bar'} | traverse('a1:b1', 'default') }}
{"c1": "foo"}
{{ {'a1': {'b1': {'c1': 'foo'}}, 'a2': 'bar'} | traverse('a2:b2', 'default') }}
"default"
json_query New in version 3000.
A port of Ansible json_query Jinja filter to make queries against JSON data using JMESPath language.  Could be used to filter pillar data, yaml maps, and together with http_query.  Depends on the jmespath Python module.
Example 1: {{ [1, 2, 3, 4, [5, 6]] | json_query('[]') }}
Example 2: {{ {"machines": [ {"name": "a", "state": "running"}, {"name": "b", "state": "stopped"}, {"name": "c", "state": "running"} ]} | json_query("machines[?state=='running'].name") }}
Example 3: {{ {"services": [ {"name": "http", "host": "1.2.3.4", "port": 80}, {"name": "smtp", "host": "1.2.3.5", "port": 25}, {"name": "ssh",  "host": "1.2.3.6", "port": 22}, ]} | json_query("services[].port") }}
Example 1: [1, 2, 3, 4, 5, 6]
Example 2: ['a', 'c']
Example 3: [80, 25, 22]
to_snake_case New in version 3000.
Converts a string from camelCase (or CamelCase) to snake_case.
Example: {{ camelsWillLoveThis | to_snake_case }}
Example: camels_will_love_this
to_camelcase New in version 3000.
Converts a string from snake_case to camelCase (or UpperCamelCase if so indicated).
Example 1: {{ snake_case_for_the_win | to_camelcase }}
Example 2: {{ snake_case_for_the_win | to_camelcase(uppercamel=True) }}
Example 1: snakeCaseForTheWin Example 2: SnakeCaseForTheWin
Networking Filters The following networking-related filters are supported:
is_ip New in version 2017.7.0.
Return if a string is a valid IP Address.
{{ '192.168.0.1' | is_ip }}
Additionally accepts the following options:
• global
• link-local
• loopback
• multicast
• private
• public
• reserved
• site-local
• unspecified
Example - test if a string is a valid loopback IP address.
{{ '192.168.0.1' | is_ip(options='loopback') }}
is_ipv4 New in version 2017.7.0.
Returns if a string is a valid IPv4 address. Supports the same options as is_ip.
{{ '192.168.0.1' | is_ipv4 }}
is_ipv6 New in version 2017.7.0.
Returns if a string is a valid IPv6 address. Supports the same options as is_ip.
{{ 'fe80::' | is_ipv6 }}
ipaddr New in version 2017.7.0.
From a list, returns only valid IP entries. Supports the same options as is_ip. The list can contains also IP interfaces/networks.
{{ ['192.168.0.1', 'foo', 'bar', 'fe80::'] | ipaddr }}
["192.168.0.1", "fe80::"]
ipv4 New in version 2017.7.0.
From a list, returns only valid IPv4 entries. Supports the same options as is_ip. The list can contains also IP interfaces/networks.
{{ ['192.168.0.1', 'foo', 'bar', 'fe80::'] | ipv4 }}
["192.168.0.1"]
ipv6 New in version 2017.7.0.
From a list, returns only valid IPv6 entries. Supports the same options as is_ip. The list can contains also IP interfaces/networks.
{{ ['192.168.0.1', 'foo', 'bar', 'fe80::'] | ipv6 }}
["fe80::"]
network_hosts New in version 2017.7.0.
Return the list of hosts within a networks. This utility works for both IPv4 and IPv6.
NOTE: When running this command with a large IPv6 network, the command will take a long time to gather all of the hosts.
{{ '192.168.0.1/30' | network_hosts }}
["192.168.0.1", "192.168.0.2"]
network_size New in version 2017.7.0.
Return the size of the network. This utility works for both IPv4 and IPv6.
{{ '192.168.0.1/8' | network_size }}
16777216
gen_mac New in version 2017.7.0.
Generates a MAC address with the defined OUI prefix.
Common prefixes:
• 00:16:3E -- Xen
• 00:18:51 -- OpenVZ
• 00:50:56 -- VMware (manually generated)
• 52:54:00 -- QEMU/KVM
• AC:DE:48 -- PRIVATE
{{ '00:50' | gen_mac }}
00:50:71:52:1C
mac_str_to_bytes New in version 2017.7.0.
Converts a string representing a valid MAC address to bytes.
{{ '00:11:22:33:44:55' | mac_str_to_bytes }}
dns_check New in version 2017.7.0.
Return the ip resolved by dns, but do not exit on failure, only raise an exception. Obeys system preference for IPv4/6 address resolution.
{{ 'www.google.com' | dns_check(port=443) }}
'172.217.3.196'
File filters is_text_file New in version 2017.7.0.
Return if a file is text.
Uses heuristics to guess whether the given file is text or binary, by reading a single block of bytes from the file.  If more than 30% of the chars in the block are non-text, or there are NUL ('x00') bytes in the block, assume this is a binary file.
{{ '/etc/salt/master' | is_text_file }}
is_binary_file New in version 2017.7.0.
Return if a file is binary.
Detects if the file is a binary, returns bool. Returns True if the file is a bin, False if the file is not and None if the file is not available.
{{ '/etc/salt/master' | is_binary_file }}
is_empty_file New in version 2017.7.0.
Return if a file is empty.
{{ '/etc/salt/master' | is_empty_file }}
file_hashsum New in version 2017.7.0.
Return the hashsum of a file.
{{ '/etc/salt/master' | file_hashsum }}
02d4ef135514934759634f10079653252c7ad594ea97bd385480c532bca0fdda
list_files New in version 2017.7.0.
Return a recursive list of files under a specific path.
{{ '/etc/salt/' | list_files | join('\n') }}
/etc/salt/master /etc/salt/proxy /etc/salt/minion /etc/salt/pillar/top.sls /etc/salt/pillar/device1.sls
path_join New in version 2017.7.0.
Joins absolute paths.
{{ '/etc/salt/' | path_join('pillar', 'device1.sls') }}
/etc/salt/pillar/device1.sls
which New in version 2017.7.0.
Python clone of /usr/bin/which.
{{ 'salt-master' | which }}
/usr/local/salt/virtualenv/bin/salt-master
Tests Saltstack extends builtin tests with these custom tests:
equalto Tests the equality between two values.
Can be used in an if statement directly:
{% if 1 is equalto(1) %} < statements > {% endif %}
If clause evaluates to True
or with the selectattr filter:
{{ [{'value': 1}, {'value': 2} , {'value': 3}] | selectattr('value', 'equalto', 3) | list }}
[{"value": 3}]
match Tests that a string matches the regex passed as an argument.
Can be used in a if statement directly:
{% if 'a' is match('[a-b]') %} < statements > {% endif %}
{{ [{'value': 'a'}, {'value': 'b'}, {'value': 'c'}] | selectattr('value', 'match', '[b-e]') | list }}
[{"value": "b"}, {"value": "c"}]
Test supports additional optional arguments: ignorecase, multiline
Escape filters regex_escape New in version 2017.7.0.
Set Theory Filters unique New in version 2017.7.0.
Jinja in Files Jinja can be used in the same way in managed files:
# redis.sls /etc/redis/redis.conf: file.managed: - source: salt://redis.conf - template: jinja - context: bind: 127.0.0.1
# lib.sls {% set port = 6379 %}
# redis.conf {% from 'lib.sls' import port with context %} port {{ port }} bind {{ bind }}
As an example, configuration was pulled from the file context and from an external template file.
NOTE: Macros and variables can be shared across templates. They should not be starting with one or more underscores, and should be managed by one of the following tags: macro, set, load_yaml, load_json, import_yaml and import_json.
Escaping Jinja Occasionally, it may be necessary to escape Jinja syntax. There are two ways to do this in Jinja. One is escaping individual variables or strings and the other is to escape entire blocks.
To escape a string commonly used in Jinja syntax such as {{, you can use the following syntax:
{{ '{{' }}
For larger blocks that contain Jinja syntax that needs to be escaped, you can use raw blocks:
{% raw %} some text that contains jinja characters that need to be escaped {% endraw %}
See the Escaping section of Jinja's documentation to learn more.
A real-word example of needing to use raw tags to escape a larger block of code is when using file.managed with the contents_pillar option to manage files that contain something like consul-template, which shares a syntax subset with Jinja. Raw blocks are necessary here because the Jinja in the pillar would be rendered before the file.managed is ever called, so the Jinja syntax must be escaped:
{% raw %} - contents_pillar: | job "example-job" { <snipped> task "example" { driver = "docker"
config { image = "docker-registry.service.consul:5000/example-job:{{key "nomad/jobs/example-job/version"}}" <snipped> {% endraw %}
Calling Salt Functions The Jinja renderer provides a shorthand lookup syntax for the salt dictionary of execution function.
# The following two function calls are equivalent. {{ salt['cmd.run']('whoami') }} {{ salt.cmd.run('whoami') }}
Debugging The show_full_context function can be used to output all variables present in the current Jinja context.
Context is: {{ show_full_context()|yaml(False) }}
Logs New in version 2017.7.0.
Yes, in Salt, one is able to debug a complex Jinja template using the logs.  For example, making the call:
{%- do salt.log.error('testing jinja logging') -%}
Will insert the following message in the minion logs:
2017-02-01 01:24:40,728 [salt.module.logmod][ERROR   ][3779] testing jinja logging
Profiling New in version 3002.
When working with a very large codebase, it becomes increasingly imperative to trace inefficiencies with state and pillar render times. The profile jinja block enables the user to get finely detailed information on the most expensive areas in the codebase.
Profiling blocks Any block of jinja code can be wrapped in a profile block.  The syntax for a profile block is {% profile as '<name>' %}<jinja code>{% endprofile %}, where <name> can be any string.  The <name> token will appear in the log at the profile level along with the render time of the block.
# /srv/salt/example.sls {%- profile as 'local data' %} {%- set local_data = {'counter': 0} %} {%- for i in range(313377) %} {%- do local_data.update({'counter': i}) %} {%- endfor %} {%- endprofile %}
test: cmd.run: - name: |- printf 'data: %s' '{{ local_data['counter'] }}'
The profile block in the example.sls state will emit the following log statement:
# salt-call --local -l profile state.apply example [...] [PROFILE ] Time (in seconds) to render profile block 'local data': 0.9385035037994385 [...]
Profiling imports Using the same logic as the profile block, the import_yaml, import_json, and import_text blocks will emit similar statements at the profile log level.
# /srv/salt/data.sls {%- set values = {'counter': 0} %} {%- for i in range(524288) %} {%- do values.update({'counter': i}) %} {%- endfor %}
data: {{ values['counter'] }}
# /srv/salt/example.sls {%- import_yaml 'data.sls' as imported %}
test: cmd.run: - name: |- printf 'data: %s' '{{ imported['data'] }}'
For import_* blocks, the profile log statement has the following form:
# salt-call --local -l profile state.apply example [...] [PROFILE ] Time (in seconds) to render import_yaml 'data.sls': 1.5500736236572266 [...]
Python Methods A powerful feature of jinja that is only hinted at in the official jinja documentation is that you can use the native python methods of the variable type. Here is the python documentation for string methods.
{% set hostname,domain = grains.id.partition('.')[::2] %}{{ hostname }}
{% set strings = grains.id.split('-') %}{{ strings[0] }}
Custom Execution Modules Custom execution modules can be used to supplement or replace complex Jinja. Many tasks that require complex looping and logic are trivial when using Python in a Salt execution module. Salt execution modules are easy to write and distribute to Salt minions.
Functions in custom execution modules are available in the Salt execution module dictionary just like the built-in execution modules:
{{ salt['my_custom_module.my_custom_function']() }}
• How to Convert Jinja Logic to an Execution Module
• Writing Execution Modules
Custom Jinja filters Given that all execution modules are available in the Jinja template, one can easily define a custom module as in the previous paragraph and use it as a Jinja filter.  However, please note that it will not be accessible through the pipe.
For example, instead of:
{{ my_variable | my_jinja_filter }}
The user will need to define my_jinja_filter function under an extension module, say my_filters and use as:
{{ salt.my_filters.my_jinja_filter(my_variable) }}
The greatest benefit is that you are able to access thousands of existing functions, e.g.:
• get the DNS AAAA records for a specific address using the dnsutil:
{{ salt.dnsutil.AAAA('www.google.com') }}