neulab/tldr huggingface dataset:
In Salt, everything is a file; there is no "magic translation" of files and file types. This means that a state file can be distributed to minions just like a plain text or binary file.
SLS Files The Salt state files are simple sets of data. Since SLS files are just data they can be represented in a number of different ways.
The default format is YAML generated from a Jinja template. This allows for the states files to have all the language constructs of Python and the simplicity of YAML.
State files can then be complicated Jinja templates that translate down to YAML, or just plain and simple YAML files.
The State files are simply common data structures such as dictionaries and lists, constructed using a templating language such as YAML.
Here is an example of a Salt State:
salt: pkg.latest: - name: salt service.running: - names: - salt-master - salt-minion - require: - pkg: salt - watch: - file: /etc/salt/minion
/etc/salt/minion: file.managed: - source: salt://salt/minion - user: root - group: root - mode: 644 - require: - pkg: salt
This short stanza will ensure that vim is installed, Salt is installed and up to date, the salt-master and salt-minion daemons are running and the Salt minion configuration file is in place. It will also ensure everything is deployed in the right order and that the Salt services are restarted when the watched file updated.
The Top File The top file controls the mapping between minions and the states which should be applied to them.
The top file specifies which minions should have which SLS files applied and which environments they should draw those SLS files from.
The top file works by specifying environments on the top-level.
Each environment contains target expressions to match minions. Finally, each target expression contains a list of Salt states to apply to matching minions:
base: '*': - salt - users - users.admin 'saltmaster.*': - match: pcre - salt.master
This above example uses the base environment which is built into the default Salt setup.
The base environment has target expressions. The first one matches all minions, and the SLS files below it apply to all minions.
The second expression is a regular expression that will match all minions with an ID matching saltmaster.* and specifies that for those minions, the salt.master state should be applied.
IMPORTANT: Since version 2014.7.0, the default matcher (when one is not explicitly defined as in the second expression in the above example) is the compound matcher. Since this matcher parses individual words in the expression, minion IDs containing spaces will not match properly using this matcher. Therefore, if your target expression is designed to match a minion ID containing spaces, it will be necessary to specify a different match type (such as glob). For example:
base: 'test minion': - match: glob - foo - bar - baz
A full table of match types available in the top file can be found here.
Reloading Modules Some Salt states require that specific packages be installed in order for the module to load. As an example the pip state module requires the pip package for proper name and version parsing.
In most of the common cases, Salt is clever enough to transparently reload the modules. For example, if you install a package, Salt reloads modules because some other module or state might require just that package which was installed.
On some edge-cases salt might need to be told to reload the modules. Consider the following state file which we'll call pep8.sls:
python-pip: cmd.run: - name: | easy_install --script-dir=/usr/bin -U pip - cwd: /
pep8: pip.installed: - require: - cmd: python-pip
The above example installs pip using easy_install from setuptools and installs pep8 using pip, which, as told earlier, requires pip to be installed system-wide. Let's execute this state:
salt-call state.apply pep8
The execution output would be something like:
---------- State: - pip Name:      pep8 Function:  installed Result:    False Comment:   State pip.installed found in sls pep8 is unavailable
Summary ------------ Succeeded: 1 Failed:    1 ------------ Total:     2
If we executed the state again the output would be:
---------- State: - pip Name:      pep8 Function:  installed Result:    True Comment:   Package was successfully installed Changes:   pep8==1.4.6: Installed
Summary ------------ Succeeded: 2 Failed:    0 ------------ Total:     2
Since we installed pip using cmd, Salt has no way to know that a system-wide package was installed.
On the second execution, since the required pip package was installed, the state executed correctly.
NOTE: Salt does not reload modules on every state run because doing so would greatly slow down state execution.
So how do we solve this edge-case? reload_modules!
reload_modules is a boolean option recognized by salt on all available states which forces salt to reload its modules once a given state finishes.
The modified state file would now be:
python-pip: cmd.run: - name: | easy_install --script-dir=/usr/bin -U pip - cwd: / - reload_modules: true
Let's run it, once:
The output is:
When the salt or salt-call CLI commands result in an error, the command will exit with a return code of 1. Error cases consist of the following:
1. Errors are encountered while running States, or any state returns a False result
2. Any exception is raised
3. In the case of remote-execution functions, when the return data is a Python dictionary with a key named either result or success, which has a value of False
Retcode Passthrough In addition to the cases listed above, if a state or remote-execution function sets a nonzero value in the retcode key of the __context__ dictionary, the command will exit with a return code of 1. For those developing custom states and execution modules, using __context__['retcode'] can be a useful way of signaling that an error has occurred:
if something_went_wrong: __context__["retcode"] = 42
This is actually how states signal that they have failed. Different cases result in different codes being set in the __context__ dictionary:
• 1 is set when any error is encountered in the state compiler (missing SLS file, etc.)
• 2 is set when any state returns a False result
• 5 is set when Pillar data fails to be compiled before running the state(s)
When the --retcode-passthrough flag is used with salt-call, then salt-call will exit with whichever retcode was set in the __context__ dictionary, rather than the default behavior which simply exits with 1 for any error condition.
UTILITY MODULES - CODE REUSE IN CUSTOM MODULES New in version 2015.5.0.
Changed in version 2016.11.0: These can now be synced to the Master for use in custom Runners, and in custom execution modules called within Pillar SLS files.
When extending Salt by writing custom (state modules), execution modules, etc., sometimes there is a need for a function to be available to more than just one kind of custom module. For these cases, Salt supports what are called "utility modules". These modules are like normal execution modules, but instead of being invoked in Salt code using __salt__, the __utils__ prefix is used instead.
For example, assuming the following simple utility module, saved to salt://_utils/foo.py
# -*- coding: utf-8 -*- """ My utils module ---------------
This module contains common functions for use in my other custom types. """
def bar(): return "baz"
Once synced to a minion, this function would be available to other custom Salt types like so:
# -*- coding: utf-8 -*- """ My awesome execution module --------------------------- """
def observe_the_awesomeness(): """ Prints information from my utility module
CLI Example:
salt '*' mymodule.observe_the_awesomeness """ return __utils__["foo.bar"]()
Utility modules, like any other kind of Salt extension, support using a __virtual__ function to conditionally load them, or load them under a different namespace. For instance, if the utility module above were named salt://_utils/mymodule.py it could be made to be loaded as the foo utility module with a __virtual__ function.
def __virtual__(): """ Load as a different name """ return "foo"
New in version 2018.3.0: Instantiating objects from classes declared in util modules works with Master side modules, such as Runners, Outputters, etc.
Also you could even write your utility modules in object oriented fashion:
# -*- coding: utf-8 -*- """ My OOP-style utils module -------------------------
class Foo(object): def __init__(self): pass
def bar(self): return "baz"
And import them into other custom modules:
import mymodule
salt '*' mymodule.observe_the_awesomeness """ foo = mymodule.Foo() return foo.bar()
These are, of course, contrived examples, but they should serve to show some of the possibilities opened up by writing utility modules. Keep in mind though that states still have access to all of the execution modules, so it is not necessary to write a utility module to make a function available to both a state and an execution module. One good use case for utility modules is one where it is necessary to invoke the same function from a custom outputter/returner, as well as an execution module.
Utility modules placed in salt://_utils/ will be synced to the minions when a highstate is run, as well as when any of the following Salt functions are called:
• saltutil.sync_utils
To sync to the Master, use either of the following:
EVENTS & REACTOR Event System The Salt Event System is used to fire off events enabling third party applications or external processes to react to behavior within Salt.  The event system uses a publish-subscribe pattern, otherwise know as pub/sub.
Event Bus The event system is comprised of a two primary components, which make up the concept of an Event Bus:
• The event sockets, which publish events
• The event library, which can listen to events and send events into the salt system
Events are published onto the event bus and event bus subscribers listen for the published events.
The event bus is used for both inter-process communication as well as network transport in Salt. Inter-process communication is provided through UNIX domain sockets (UDX).
The Salt Master and each Salt Minion has their own event bus.
Event types Salt Master Events These events are fired on the Salt Master event bus. This list is not comprehensive.
Authentication events salt/auth Fired when a minion performs an authentication check with the master.
• id -- The minion ID.
• act -- The current status of the minion key: accept, pend, reject.
• pub -- The minion public key.
NOTE: Minions fire auth events on fairly regular basis for a number of reasons.  Writing reactors to respond to events through the auth cycle can lead to infinite reactor event loops (minion tries to auth, reactor responds by doing something that generates another auth event, minion sends auth event, etc.). Consider reacting to salt/key or salt/minion/<MID>/start or firing a custom event tag instead.
Start events salt/minion/<MID>/start Fired every time a minion connects to the Salt master.
Variables id -- The minion ID.
Key events salt/key Fired when accepting and rejecting minions keys on the Salt master.  These happen as a result of actions undertaken by the salt-key command.
• act -- The new status of the minion key: accept, delete,
WARNING: If a master is in auto_accept mode, salt/key events will not be fired when the keys are accepted.  In addition, pre-seeding keys (like happens through Salt-Cloud) will not cause firing of these events.
Job events salt/job/<JID>/new Fired as a new job is sent out to minions.
• jid -- The job ID.
• tgt -- The target of the job: *, a minion ID, G@os_family:RedHat, etc.
• tgt_type -- The type of targeting used: glob, grain, compound, etc.
• fun -- The function to run on minions: test.version, network.interfaces, etc.
• arg -- A list of arguments to pass to the function that will be called.
• minions -- A list of minion IDs that Salt expects will return data for this job.
• user -- The name of the user that ran the command as defined in Salt's Publisher ACL or external auth.
salt/job/<JID>/ret/<MID> Fired each time a minion returns data for a job.
• retcode -- The return code for the job.
• fun -- The function the minion ran. E.g., test.version.
• return -- The data returned from the execution module.
salt/job/<JID>/prog/<MID>/<RUN NUM> Fired each time a each function in a state run completes execution. Must be enabled using the state_events option.
• data -- The data returned from the state module function.
Runner Events salt/run/<JID>/new Fired as a runner begins execution
• fun -- The name of the runner function, with runner. prepended to it (e.g. runner.jobs.lookup_jid)
• fun_args -- The arguments passed to the runner function (e.g.  ['20160829225914848058'])
• user -- The user who executed the runner (e.g. root)
salt/run/<JID>/ret Fired when a runner function returns
• return -- The data returned by the runner function
salt/run/<JID>/args New in version 2016.11.0.
Fired by the state.orchestrate runner
• name -- The ID declaration for the orchestration job (i.e. the line above salt.state, salt.function, salt.runner, etc.)
• type -- The type of orchestration job being run (e.g. state)
• tgt -- The target expression (e.g. *). Included for state and function types only.
• args -- The args passed to the orchestration job. Note: for state and function types, also includes a tgt_type value which shows what kind of match (glob, pcre, etc.) was used.  This value was named expr_form in the 2016.11 release cycle but has been renamed to tgt_type in 2017.7.0 for consistency with other events.
Presence Events salt/presence/present Events fired on a regular interval about currently connected, newly connected, or recently disconnected minions. Requires the presence_events setting to be enabled.
Variables present -- A list of minions that are currently connected to the Salt master.
salt/presence/change Fired when the Presence system detects new minions connect or disconnect.
• new -- A list of minions that have connected since the last presence event.
• lost -- A list of minions that have disconnected since the last presence event.
Cloud Events Unlike other Master events, salt-cloud events are not fired on behalf of a Salt Minion. Instead, salt-cloud events are fired on behalf of a VM. This is because the minion-to-be may not yet exist to fire events to or also may have been destroyed.
This behavior is reflected by the name variable in the event data for salt-cloud events as compared to the id variable for Salt Minion-triggered events.
salt/cloud/<VM NAME>/creating Fired when salt-cloud starts the VM creation process.
• name -- the name of the VM being created.
• event -- description of the event.
• provider -- the cloud provider of the VM being created.
• profile -- the cloud profile for the VM being created.
salt/cloud/<VM NAME>/deploying Fired when the VM is available and salt-cloud begins deploying Salt to the new VM.
• kwargs -- options available as the deploy script is invoked: conf_file, deploy_command, display_ssh_output, host, keep_tmp, key_filename, make_minion, minion_conf, name, parallel, preseed_minion_keys, script, script_args, script_env, sock_dir, start_action, sudo, tmp_dir, tty, username
salt/cloud/<VM NAME>/requesting Fired when salt-cloud sends the request to create a new VM.
• location -- the location of the VM being requested.
• kwargs -- options available as the VM is being requested: Action, ImageId, InstanceType, KeyName, MaxCount, MinCount, SecurityGroup.1
salt/cloud/<VM NAME>/querying Fired when salt-cloud queries data for a new instance.
• instance_id -- the ID of the new VM.
salt/cloud/<VM NAME>/tagging Fired when salt-cloud tags a new instance.
• tags -- tags being set on the new instance.
salt/cloud/<VM NAME>/waiting_for_ssh Fired while the salt-cloud deploy process is waiting for ssh to become available on the new instance.
• ip_address -- IP address of the new instance.
salt/cloud/<VM NAME>/deploy_script Fired once the deploy script is finished.
Variables event -- description of the event.
salt/cloud/<VM NAME>/created Fired once the new instance has been fully created.
• instance_id -- the ID of the new instance.
salt/cloud/<VM NAME>/destroying Fired when salt-cloud requests the destruction of an instance.
salt/cloud/<VM NAME>/destroyed Fired when an instance has been destroyed.
Listening for Events Salt's event system is used heavily within Salt and it is also written to integrate heavily with existing tooling and scripts. There is a variety of ways to consume it.
From the CLI The quickest way to watch the event bus is by calling the state.event runner:
salt-run state.event pretty=True
That runner is designed to interact with the event bus from external tools and shell scripts. See the documentation for more examples.
Remotely via the REST API Salt's event bus can be consumed salt.netapi.rest_cherrypy.app.Events as an HTTP stream from external tools or services.
curl -SsNk https://salt-api.example.com:8000/events?token=05A3
From Python Python scripts can access the event bus only as the same system user that Salt is running as.
The event system is accessed via the event library and can only be accessed by the same system user that Salt is running as. To listen to events a SaltEvent object needs to be created and then the get_event function needs to be run. The SaltEvent object needs to know the location that the Salt Unix sockets are kept. In the configuration this is the sock_dir option. The sock_dir option defaults to "/var/run/salt/master" on most systems.
The following code will check for a single event:
import salt.config import salt.utils.event
opts = salt.config.client_config("/etc/salt/master")
event = salt.utils.event.get_event( "master", sock_dir=opts["sock_dir"], transport=opts["transport"], opts=opts )
data = event.get_event()
Events will also use a "tag". Tags allow for events to be filtered by prefix.  By default all events will be returned. If only authentication events are desired, then pass the tag "salt/auth".
The get_event method has a default poll time assigned of 5 seconds. To change this time set the "wait" option.
The following example will only listen for auth events and will wait for 10 seconds instead of the default 5.
data = event.get_event(wait=10, tag="salt/auth")
To retrieve the tag as well as the event data, pass full=True:
evdata = event.get_event(wait=10, tag="salt/job", full=True)
tag, data = evdata["tag"], evdata["data"]
Instead of looking for a single event, the iter_events method can be used to make a generator which will continually yield salt events.
The iter_events method also accepts a tag but not a wait time:
for data in event.iter_events(tag="salt/auth"): print(data)
And finally event tags can be globbed, such as they can be in the Reactor, using the fnmatch library.
import fnmatch
sevent = salt.utils.event.get_event( "master", sock_dir=opts["sock_dir"], transport=opts["transport"], opts=opts )
while True: ret = sevent.get_event(full=True) if ret is None: continue
if fnmatch.fnmatch(ret["tag"], "salt/job/*/ret/*"): do_something_with_job_return(ret["data"])
Firing Events It is possible to fire events on either the minion's local bus or to fire events intended for the master.
To fire a local event from the minion on the command line call the event.fire execution function:
salt-call event.fire '{"data": "message to be sent in the event"}' 'tag'
To fire an event to be sent up to the master from the minion call the event.send execution function. Remember YAML can be used at the CLI in function arguments:
salt-call event.send 'myco/mytag/success' '{success: True, message: "It works!"}'
If a process is listening on the minion, it may be useful for a user on the master to fire an event to it. An example of listening local events on a minion on a non-Windows system:
# Job on minion import salt.utils.event
opts = salt.config.minion_config("/etc/salt/minion") event = salt.utils.event.MinionEvent(opts)
for evdata in event.iter_events(match_type="regex", tag="custom/.*"): # do your processing here... ...
And an example of listening local events on a Windows system:
opts = salt.config.minion_config(salt.minion.DEFAULT_MINION_OPTS) event = salt.utils.event.MinionEvent(opts)
salt minionname event.fire '{"data": "message for the minion"}' 'customtag/african/unladen'
Firing Events from Python From Salt execution modules Events can be very useful when writing execution modules, in order to inform various processes on the master when a certain task has taken place. This is easily done using the normal cross-calling syntax:
# /srv/salt/_modules/my_custom_module.py
def do_something(): """ Do something and fire an event to the master when finished
salt '*' my_custom_module:do_something """ # do something! __salt__["event.send"]( "myco/my_custom_module/finished", {"finished": True, "message": "The something is finished!"}, )
From Custom Python Scripts Firing events from custom Python code is quite simple and mirrors how it is done at the CLI:
import salt.client
caller = salt.client.Caller()
ret = caller.cmd( "event.send", "myco/event/success", {"success": True, "message": "It works!"} )
if not ret: # the event could not be sent, process the error here ...
Beacons Beacons let you use the Salt event system to monitor non-Salt processes. The beacon system allows the minion to hook into a variety of system processes and continually monitor these processes. When monitored activity occurs in a system process, an event is sent on the Salt event bus that can be used to trigger a reactor.
Salt beacons can currently monitor and send Salt events for many system activities, including:
• file system changes
• system load
• service status
• shell activity, such as user login
• network and disk usage
See beacon modules for a current list.
NOTE: Salt beacons are an event generation mechanism. Beacons leverage the Salt reactor system to make changes when beacon events occur.
Configuring Beacons Salt beacons do not require any changes to the system components that are being monitored, everything is configured using Salt.
Beacons are typically enabled by placing a beacons: top level block in /etc/salt/minion or any file in /etc/salt/minion.d/ such as /etc/salt/minion.d/beacons.conf or add it to pillars for that minion:
beacons: inotify: - files: /etc/important_file: {} /opt: {}
The beacon system, like many others in Salt, can also be configured via the minion pillar, grains, or local config file.
NOTE: The inotify beacon only works on OSes that have inotify kernel support.  Currently this excludes FreeBSD, macOS, and Windows.
All beacon configuration is done using list based configuration.
New in version Neon.
Multiple copies of a particular Salt beacon can be configured by including the beacon_module parameter in the beacon configuration.
beacons: watch_important_file: - files: /etc/important_file: {} - beacon_module: inotify watch_another_file: - files: /etc/another_file: {} - beacon_module: inotify
Beacon Monitoring Interval Beacons monitor on a 1-second interval by default. To set a different interval, provide an interval argument to a beacon. The following beacons run on 5- and 10-second intervals:
beacons: inotify: - files: /etc/important_file: {} /opt: {} - interval: 5 - disable_during_state_run: True load: - averages: 1m: - 0.0 - 2.0 5m: - 0.0 - 1.5 15m: - 0.1 - 1.0 - interval: 10
Avoiding Event Loops It is important to carefully consider the possibility of creating a loop between a reactor and a beacon. For example, one might set up a beacon which monitors whether a file is read which in turn fires a reactor to run a state which in turn reads the file and re-fires the beacon.
To avoid these types of scenarios, the disable_during_state_run argument may be set. If a state run is in progress, the beacon will not be run on its regular interval until the minion detects that the state run has completed, at which point the normal beacon interval will resume.
beacons: inotify: - files: /etc/important_file: {} - disable_during_state_run: True
NOTE: For beacon writers:  If you need extra stuff to happen, like closing file handles for the disable_during_state_run to actually work, you can add a close() function to the beacon to run those extra things. See the inotify beacon.
Beacon Example This example demonstrates configuring the inotify beacon to monitor a file for changes, and then restores the file to its original contents if a change was made.
NOTE: The inotify beacon requires Pyinotify on the minion, install it using salt myminion pkg.install python-inotify.
Create Watched File Create the file named /etc/important_file and add some simple content:
important_config: True
Add Beacon Configs to Minion On the Salt minion, add the following configuration to /etc/salt/minion.d/beacons.conf:
beacons: inotify: - files: /etc/important_file: mask: - modify - disable_during_state_run: True
Save the configuration file and restart the minion service. The beacon is now set up to notify salt upon modifications made to the file.
NOTE: The disable_during_state_run: True parameter prevents the inotify beacon from generating reactor events due to salt itself modifying the file.
View Events on the Master On your Salt master, start the event runner using the following command:
salt-run state.event pretty=true
This runner displays events as they are received by the master on the Salt event bus. To test the beacon you set up in the previous section, make and save a modification to /etc/important_file. You'll see an event similar to the following on the event bus:
{ "_stamp": "2015-09-09T15:59:37.972753", "data": { "change": "IN_IGNORED", "id": "larry", "path": "/etc/important_file" }, "tag": "salt/beacon/larry/inotify//etc/important_file" }
This indicates that the event is being captured and sent correctly. Now you can create a reactor to take action when this event occurs.
Create a Reactor This reactor reverts the file named /etc/important_file to the contents provided by salt each time it is modified.
Reactor SLS On your Salt master, create a file named /srv/reactor/revert.sls.
NOTE: If the /srv/reactor directory doesn't exist, create it.
mkdir -p /srv/reactor
Add the following to /srv/reactor/revert.sls:
revert-file: local.state.apply: - tgt: {{ data['data']['id'] }} - arg: - maintain_important_file
NOTE: In addition to setting disable_during_state_run: True for an inotify beacon whose reaction is to modify the watched file, it is important to ensure the state applied is also idempotent.
NOTE: The expression {{ data['data']['id'] }} is correct as it matches the event structure shown above.
State SLS Create the state sls file referenced by the reactor sls file.  This state file will be located at /srv/salt/maintain_important_file.sls.
important_file: file.managed: - name: /etc/important_file - contents: | important_config: True
Master Config Configure the master to map the inotify beacon event to the revert reaction in /etc/salt/master.d/reactor.conf:
reactor: - salt/beacon/*/inotify//etc/important_file: - /srv/reactor/revert.sls
NOTE: You can have only one top level reactor section, so if one already exists, add this code to the existing section. See here to learn more about reactor SLS syntax.
Start the Salt Master in Debug Mode To help with troubleshooting, start the Salt master in debug mode:
service salt-master stop salt-master -l debug
When debug logging is enabled, event and reactor data are displayed so you can discover syntax and other issues.
Trigger the Reactor On your minion, make and save another change to /etc/important_file. On the Salt master, you'll see debug messages that indicate the event was received and the state.apply job was sent. When you inspect the file on the minion, you'll see that the file contents have been restored to important_config: True.
All beacons are configured using a similar process of enabling the beacon, writing a reactor SLS (and state SLS if needed), and mapping a beacon event to the reactor SLS.
Writing Beacon Plugins Beacon plugins use the standard Salt loader system, meaning that many of the constructs from other plugin systems holds true, such as the __virtual__ function.
The important function in the Beacon Plugin is the beacon function. When the beacon is configured to run, this function will be executed repeatedly by the minion. The beacon function therefore cannot block and should be as lightweight as possible. The beacon also must return a list of dicts, each dict in the list will be translated into an event on the master.
Beacons may also choose to implement a validate function which takes the beacon configuration as an argument and ensures that it is valid prior to continuing. This function is called automatically by the Salt loader when a beacon is loaded.
Please see the inotify beacon as an example.
The beacon Function The beacons system will look for a function named beacon in the module. If this function is not present then the beacon will not be fired. This function is called on a regular basis and defaults to being called on every iteration of the minion, which can be tens to hundreds of times a second. This means that the beacon function cannot block and should not be CPU or IO intensive.
The beacon function will be passed in the configuration for the executed beacon. This makes it easy to establish a flexible configuration for each called beacon. This is also the preferred way to ingest the beacon's configuration as it allows for the configuration to be dynamically updated while the minion is running by configuring the beacon in the minion's pillar.
The Beacon Return The information returned from the beacon is expected to follow a predefined structure. The returned value needs to be a list of dictionaries (standard python dictionaries are preferred, no ordered dicts are needed).
The dictionaries represent individual events to be fired on the minion and master event buses. Each dict is a single event. The dict can contain any arbitrary keys but the 'tag' key will be extracted and added to the tag of the fired event.
The return data structure would look something like this:
[{"changes": ["/foo/bar"], "tag": "foo"}, {"changes": ["/foo/baz"], "tag": "bar"}]
Calling Execution Modules Execution modules are still the preferred location for all work and system interaction to happen in Salt. For this reason the __salt__ variable is available inside the beacon.
Please be careful when calling functions in __salt__, while this is the preferred means of executing complicated routines in Salt not all of the execution modules have been written with beacons in mind. Watch out for execution modules that may be CPU intense or IO bound. Please feel free to add new execution modules and functions to back specific beacons.
Distributing Custom Beacons Custom beacons can be distributed to minions via the standard methods, see Modular Systems.
Reactor System Salt's Reactor system gives Salt the ability to trigger actions in response to an event. It is a simple interface to watching Salt's event bus for event tags that match a given pattern and then running one or more commands in response.
This system binds sls files to event tags on the master. These sls files then define reactions. This means that the reactor system has two parts. First, the reactor option needs to be set in the master configuration file.  The reactor option allows for event tags to be associated with sls reaction files. Second, these reaction files use highdata (like the state system) to define reactions to be executed.
Event System A basic understanding of the event system is required to understand reactors.  The event system is a local ZeroMQ PUB interface which fires salt events. This event bus is an open system used for sending information notifying Salt and other systems about operations.
The event system fires events with a very specific criteria. Every event has a tag. Event tags allow for fast top-level filtering of events. In addition to the tag, each event has a data structure. This data structure is a dictionary, which contains information about the event.
Mapping Events to Reactor SLS Files Reactor SLS files and event tags are associated in the master config file.  By default this is /etc/salt/master, or /etc/salt/master.d/reactor.conf.
New in version 2014.7.0: Added Reactor support for salt:// file paths.
In the master config section 'reactor:' is a list of event tags to be matched and each event tag has a list of reactor SLS files to be run.
reactor:                            # Master config section "reactor"
- 'salt/minion/*/start':          # Match tag "salt/minion/*/start" - /srv/reactor/start.sls        # Things to do when a minion starts - /srv/reactor/monitor.sls      # Other things to do
- 'salt/cloud/*/destroyed':       # Globs can be used to match tags - /srv/reactor/destroy/*.sls    # Globs can be used to match file names
- 'myco/custom/event/tag':        # React to custom event tags - salt://reactor/mycustom.sls   # Reactor files can come from the salt fileserver
NOTE: In the above example, salt://reactor/mycustom.sls refers to the base environment. To pull this file from a different environment, use the querystring syntax (e.g. salt://reactor/mycustom.sls?saltenv=reactor).
Reactor SLS files are similar to State and Pillar SLS files. They are by default YAML + Jinja templates and are passed familiar context variables. Click here for more detailed information on the variables available in Jinja templating.
Here is the SLS for a simple reaction:
This simple reactor file uses Jinja to further refine the reaction to be made.  If the id in the event data is mysql1 (in other words, if the name of the minion is mysql1) then the following reaction is defined.  The same data structure and compiler used for the state system is used for the reactor system. The only difference is that the data is matched up to the salt command API and the runner system.  In this example, a command is published to the mysql1 minion with a function of state.apply, which performs a highstate. Similarly, a runner can be called:
{% if data['data']['custom_var'] == 'runit' %} call_runit_orch: runner.state.orchestrate: - args: - mods: orchestrate.runit {% endif %}
This example will execute the state.orchestrate runner and intiate an execution of the runit orchestrator located at /srv/salt/orchestrate/runit.sls.
Types of Reactions ┌───────┬────────────────────────────┐ │Name   │ Description                │ ├───────┼────────────────────────────┤ │local  │ Runs a remote-execution    │ │       │ function on targeted       │ │       │ minions                    │ ├───────┼────────────────────────────┤ │runner │ Executes a runner function │ ├───────┼────────────────────────────┤ │wheel  │ Executes a wheel function  │ │       │ on the master              │ ├───────┼────────────────────────────┤ │caller │ Runs a remote-execution    │ │       │ function on a masterless   │ │       │ minion                     │ └───────┴────────────────────────────┘
NOTE: The local and caller reaction types will likely be renamed in a future release. These reaction types were named after Salt's internal client interfaces, and are not intuitively named. Both local and caller will continue to work in Reactor SLS files, however.
Where to Put Reactor SLS Files Reactor SLS files can come both from files local to the master, and from any of backends enabled via the fileserver_backend config option. Files placed in the Salt fileserver can be referenced using a salt:// URL, just like they can in State SLS files.
It is recommended to place reactor and orchestrator SLS files in their own uniquely-named subdirectories such as orch/, orchestrate/, react/, reactor/, etc., to keep them organized.
Writing Reactor SLS The different reaction types were developed separately and have historically had different methods for passing arguments. For the 2017.7.2 release a new, unified configuration schema has been introduced, which applies to all reaction types.
The old config schema will continue to be supported, and there is no plan to deprecate it at this time.
Local Reactions A local reaction runs a remote-execution function on the targeted minions.
The old config schema required the positional and keyword arguments to be manually separated by the user under arg and kwarg parameters. However, this is not very user-friendly, as it forces the user to distinguish which type of argument is which, and make sure that positional arguments are ordered properly. Therefore, the new config schema is recommended if the master is running a supported release.
The below two examples are equivalent:
┌─────────────────────────────────────┬─────────────────────────────────────┐ │Supported in 2017.7.2 and            │ Supported in all releases           │ │later                                │                                     │ └─────────────────────────────────────┴─────────────────────────────────────┘
│                                     │                                     │ │          install_zsh:               │           install_zsh:              │ │            local.state.single:      │             local.state.single:     │ │              - tgt: 'kernel:Linux'  │               - tgt: 'kernel:Linux' │ │              - tgt_type: grain      │               - tgt_type: grain     │ │              - args:                │               - arg:                │ │                - fun: pkg.installed │                 - pkg.installed     │ │                - name: zsh          │                 - zsh               │ │                - fromrepo: updates  │               - kwarg:              │ │                                     │                   fromrepo: updates │ └─────────────────────────────────────┴─────────────────────────────────────┘
This reaction would be equivalent to running the following Salt command:
salt -G 'kernel:Linux' state.single pkg.installed name=zsh fromrepo=updates
NOTE: Any other parameters in the LocalClient().cmd_async() method can be passed at the same indentation level as tgt.
NOTE: tgt_type is only required when the target expression defined in tgt uses a target type other than a minion ID glob.
The tgt_type argument was named expr_form in releases prior to 2017.7.0.
Runner Reactions Runner reactions execute runner functions locally on the master.
The old config schema called for passing arguments to the reaction directly under the name of the runner function. However, this can cause unpredictable interactions with the Reactor system's internal arguments. It is also possible to pass positional and keyword arguments under arg and kwarg like above in local reactions, but as noted above this is not very user-friendly. Therefore, the new config schema is recommended if the master is running a supported release.
┌────────────────────────────────────────────────────────┬─────────────────────────────────────────────────────────┐
│Supported in 2017.7.2 and                               │ Supported in all releases                               │
│later                                                   │                                                         │
├────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────┤
│                                                        │                                                         │
│          deploy_app:                                   │           deploy_app:                                   │
│            runner.state.orchestrate:                   │             runner.state.orchestrate:                   │
│              - args:                                   │               - mods: orchestrate.deploy_app            │
│                - mods: orchestrate.deploy_app          │               - kwarg:                                  │
│                - pillar:                               │                   pillar:                               │
│                    event_tag: {{ tag }}                │                     event_tag: {{ tag }}                │
│                    event_data: {{ data['data']|json }} │                     event_data: {{ data['data']|json }} │
└────────────────────────────────────────────────────────┴─────────────────────────────────────────────────────────┘
Assuming that the event tag is foo, and the data passed to the event is {'bar': 'baz'}, then this reaction is equivalent to running the following Salt command:
salt-run state.orchestrate mods=orchestrate.deploy_app pillar='{"event_tag": "foo", "event_data": {"bar": "baz"}}'
Wheel Reactions Wheel reactions run wheel functions locally on the master.
Like runner reactions, the old config schema called for wheel reactions to have arguments passed directly under the name of the wheel function (or in arg or kwarg parameters).
┌──────────────────────────────────────────┬─────────────────────────────────────────┐ │Supported in 2017.7.2 and                 │ Supported in all releases               │ │later                                     │                                         │ ├──────────────────────────────────────────┼─────────────────────────────────────────┤ │                                          │                                         │ │          remove_key:                     │           remove_key:                   │ │            wheel.key.delete:             │             wheel.key.delete:           │ │              - args:                     │               - match: {{ data['id'] }} │ │                - match: {{ data['id'] }} │                                         │ └──────────────────────────────────────────┴─────────────────────────────────────────┘
Caller Reactions Caller reactions run remote-execution functions on a minion daemon's Reactor system. To run a Reactor on the minion, it is necessary to configure the Reactor Engine in the minion config file, and then setup your watched events in a reactor section in the minion config file as well.
NOTE: Masterless Minions use this Reactor
This is the only way to run the Reactor if you use masterless minions.
Both the old and new config schemas involve passing arguments under an args parameter. However, the old config schema only supports positional arguments.  Therefore, the new config schema is recommended if the masterless minion is running a supported release.
┌─────────────────────────────────┬────────────────────────────────┐ │Supported in 2017.7.2 and        │ Supported in all releases      │ │later                            │                                │ ├─────────────────────────────────┼────────────────────────────────┤ │                                 │                                │ │          touch_file:            │           touch_file:          │ │            caller.file.touch:   │             caller.file.touch: │ │              - args:            │               - args:          │ │                - name: /tmp/foo │                 - /tmp/foo     │ └─────────────────────────────────┴────────────────────────────────┘
This reaction is equivalent to running the following Salt command:
salt-call file.touch name=/tmp/foo
Best Practices for Writing Reactor SLS Files The Reactor works as follows:
1. The Salt Reactor watches Salt's event bus for new events.
2. Each event's tag is matched against the list of event tags configured under the reactor section in the Salt Master config.
3. The SLS files for any matches are rendered into a data structure that represents one or more function calls.
4. That data structure is given to a pool of worker threads for execution.
Matching and rendering Reactor SLS files is done sequentially in a single process. For that reason, reactor SLS files should contain few individual reactions (one, if at all possible). Also, keep in mind that reactions are fired asynchronously (with the exception of caller) and do not support requisites.
Complex Jinja templating that calls out to slow remote-execution or runner functions slows down the rendering and causes other reactions to pile up behind the current one. The worker pool is designed to handle complex and long-running processes like orchestration jobs.
Therefore, when complex tasks are in order, orchestration is a natural fit. Orchestration SLS files can be more complex, and use requisites. Performing a complex task using orchestration lets the Reactor system fire off the orchestration job and proceed with processing other reactions.
Jinja Context Reactor SLS files only have access to a minimal Jinja context. grains and pillar are not available. The salt object is available for calling remote-execution or runner functions, but it should be used sparingly and only for quick tasks for the reasons mentioned above.
In addition to the salt object, the following variables are available in the Jinja context:
• tag - the tag from the event that triggered execution of the Reactor SLS file
• data - the event's data dictionary
The data dict will contain an id key containing the minion ID, if the event was fired from a minion, and a data key containing the data passed to the event.
Advanced State System Capabilities Reactor SLS files, by design, do not support requisites, ordering, onlyif/unless conditionals and most other powerful constructs from Salt's State system.
Complex Master-side operations are best performed by Salt's Orchestrate system so using the Reactor to kick off an Orchestrate run is a very common pairing.
# /etc/salt/master.d/reactor.conf # A custom event containing: {"foo": "Foo!", "bar: "bar*", "baz": "Baz!"} reactor: - my/custom/event: - /srv/reactor/some_event.sls
# /srv/reactor/some_event.sls invoke_orchestrate_file: runner.state.orchestrate: - args: - mods: orchestrate.do_complex_thing - pillar: event_tag: {{ tag }} event_data: {{ data|json }}
# /srv/salt/orchestrate/do_complex_thing.sls {% set tag = salt.pillar.get('event_tag') %} {% set data = salt.pillar.get('event_data') %}
# Pass data from the event to a custom runner function. # The function expects a 'foo' argument. do_first_thing: salt.runner: - name: custom_runner.custom_function - foo: {{ data.foo }}
# Wait for the runner to finish then send an execution to minions. # Forward some data from the event down to the minion's state run. do_second_thing: salt.state: - tgt: {{ data.bar }} - sls: - do_thing_on_minion - kwarg: pillar: baz: {{ data.baz }} - require: - salt: do_first_thing
Beacons and Reactors An event initiated by a beacon, when it arrives at the master will be wrapped inside a second event, such that the data object containing the beacon information will be data['data'], rather than data.
For example, to access the id field of the beacon event in a reactor file, you will need to reference {{ data['data']['id'] }} rather than {{ data['id'] }} as for events initiated directly on the event bus.
Similarly, the data dictionary attached to the event would be located in {{ data['data']['data'] }} instead of {{ data['data'] }}.
See the beacon documentation for examples.
Manually Firing an Event From the Master Use the event.send runner:
salt-run event.send foo '{orchestrate: refresh}'
From the Minion To fire an event to the master from a minion, call event.send:
salt-call event.send foo '{orchestrate: refresh}'
To fire an event to the minion's local event bus, call event.fire:
salt-call event.fire '{orchestrate: refresh}' foo
Referencing Data Passed in Events Assuming any of the above examples, any reactor SLS files triggered by watching the event tag foo will execute with {{ data['data']['orchestrate'] }} equal to 'refresh'.
Getting Information About Events The best way to see exactly what events have been fired and what data is available in each event is to use the state.event runner.
SEE ALSO: Common Salt Events
salt/job/20150213001905721678/new       { "_stamp": "2015-02-13T00:19:05.724583", "arg": [], "fun": "test.ping", "jid": "20150213001905721678", "minions": [ "jerry" ], "tgt": "*", "tgt_type": "glob", "user": "root" } salt/job/20150213001910749506/ret/jerry { "_stamp": "2015-02-13T00:19:11.136730", "cmd": "_return", "fun": "saltutil.find_job", "fun_args": [ "20150213001905721678" ], "id": "jerry", "jid": "20150213001910749506", "retcode": 0, "return": {}, "success": true }
Debugging the Reactor The best window into the Reactor is to run the master in the foreground with debug logging enabled. The output will include when the master sees the event, what the master does in response to that event, and it will also include the rendered SLS file (or any errors generated while rendering the SLS file).
1. Stop the master.
2. Start the master manually:
3. Look for log entries in the form:
[DEBUG   ] Gathering reactors for tag foo/bar [DEBUG   ] Compiling reactions for tag foo/bar [DEBUG   ] Rendered data from file: /path/to/the/reactor_file.sls: <... Rendered output appears here. ...>