7 KiB
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
master_events
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.runners.state.event>
:
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
= salt.config.client_config("/etc/salt/master")
opts
= salt.utils.event.get_event(
event "master", sock_dir=opts["sock_dir"], transport=opts["transport"], opts=opts
)
= event.get_event() data
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.
= event.get_event(wait=10, tag="salt/auth") data
To retrieve the tag as well as the event data, pass
full=True
:
= event.get_event(wait=10, tag="salt/job", full=True)
evdata
= evdata["tag"], evdata["data"] tag, 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
import salt.config
import salt.utils.event
= salt.config.client_config("/etc/salt/master")
opts
= salt.utils.event.get_event(
sevent "master", sock_dir=opts["sock_dir"], transport=opts["transport"], opts=opts
)
while True:
= sevent.get_event(full=True)
ret if ret is None:
continue
if fnmatch.fnmatch(ret["tag"], "salt/job/*/ret/*"):
"data"]) do_something_with_job_return(ret[
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 <salt.modules.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 <salt.modules.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
= salt.config.minion_config("/etc/salt/minion")
opts = salt.utils.event.MinionEvent(opts)
event
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:
# Job on minion
import salt.utils.event
= salt.config.minion_config(salt.minion.DEFAULT_MINION_OPTS)
opts = salt.utils.event.MinionEvent(opts)
event
for evdata in event.iter_events(match_type="regex", tag="custom/.*"):
# do your processing here...
...
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
CLI Example::
salt '*' my_custom_module:do_something
"""
# do something!
"event.send"](
__salt__["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
= salt.client.Caller()
caller
= caller.cmd(
ret "event.send", "myco/event/success", {"success": True, "message": "It works!"}
)
if not ret:
# the event could not be sent, process the error here
...