Salt execution modules are the functions called by the salt command.
Note
Salt execution modules are different from state modules and cannot be
called directly within state files. You must use the module
state module to call execution modules within state runs.
See also
Salt ships with many modules that cover a wide variety of tasks.
Writing Salt execution modules is straightforward.
A Salt execution modules is a Python or Cython module
placed in a directory called _modules/
within the file_roots
as specified by the master config file. By
default this is /srv/salt/_modules on Linux systems.
Modules placed in _modules/
will be synced to the minions when any of the following
Salt functions are called:
Note that a module's default name is its filename
(i.e. foo.py
becomes module foo
), but that its name can be overridden
by using a __virtual__ function.
If a Salt module has errors and cannot be imported, the Salt minion will continue to load without issue and the module with errors will simply be omitted.
If adding a Cython module the file must be named <modulename>.pyx
so that
the loader knows that the module needs to be imported as a Cython module. The
compilation of the Cython module is automatic and happens when the minion
starts, so only the *.pyx
file is required.
All of the Salt execution modules are available to each other and modules can call functions available in other execution modules.
The variable __salt__
is packed into the modules after they are loaded into
the Salt minion.
The __salt__
variable is a Python dictionary
containing all of the Salt functions. Dictionary keys are strings representing the
names of the modules and the values are the functions themselves.
Salt modules can be cross-called by accessing the value in the __salt__
dict:
def foo(bar):
return __salt__['cmd.run'](bar)
This code will call the run function in the cmd
and pass the argument
bar
to it.
When interacting with execution modules often it is nice to be able to read information dynamically about the minion or to load in configuration parameters for a module.
Salt allows for different types of data to be loaded into the modules by the minion.
The values detected by the Salt Grains on the minion are available in a
dict named __grains__
and can be accessed
from within callable objects in the Python modules.
To see the contents of the grains dictionary for a given system in your deployment
run the grains.items()
function:
salt 'hostname' grains.items --output=pprint
Any value in a grains dictionary can be accessed as any other Python dictionary. For
example, the grain representing the minion ID is stored in the id
key and from
an execution module, the value would be stored in __grains__['id']
.
Since parameters for configuring a module may be desired, Salt allows for configuration information from the minion configuration file to be passed to execution modules.
Since the minion configuration file is a YAML document, arbitrary configuration
data can be passed in the minion config that is read by the modules. It is therefore
strongly recommended that the values passed in the configuration file match
the module name. A value intended for the test
execution module should be named
test.<value>
.
The test execution module contains usage of the module configuration and the default
configuration file for the minion contains the information and format used to
pass data to the modules. salt.modules.test
, conf/minion
.
Since execution module functions can return different data, and the way the data is printed can greatly change the presentation, Salt has a printout configuration.
When writing a module the __outputter__
dictionary can be declared in the module.
The __outputter__
dictionary contains a mapping of function name to Salt
Outputter.
__outputter__ = {
'run': 'txt'
}
This will ensure that the text outputter is used.
Sometimes an execution module should be presented in a generic way. A good example of this can be found in the package manager modules. The package manager changes from one operating system to another, but the Salt execution module that interfaces with the package manager can be presented in a generic way.
The Salt modules for package managers all contain a __virtual__
function
which is called to define what systems the module should be loaded on.
The __virtual__
function is used to return either a
string or False
. If
False is returned then the module is not loaded, if a string is returned then
the module is loaded with the name of the string.
This means that the package manager modules can be presented as the pkg
module
regardless of what the actual module is named.
The package manager modules are the best example of using the __virtual__
function. Some examples:
Note
Modules which return a string from __virtual__
that is already used by a module that
ships with Salt will _override_ the stock module.
Salt execution modules are documented. The sys.doc()
function will return the
documentation for all available modules:
salt '*' sys.doc
The sys.doc
function simply prints out the docstrings found in the modules; when
writing Salt execution modules, please follow the formatting conventions for docstrings as
they appear in the other modules.
It is strongly suggested that all Salt modules have documentation added.
To add documentation add a Python docstring to the function.
def spam(eggs):
'''
A function to make some spam with eggs!
CLI Example::
salt '*' test.spam eggs
'''
return eggs
Now when the sys.doc call is executed the docstring will be cleanly returned to the calling terminal.
Documentation added to execution modules in docstrings will automatically be added to the online web-based documentation.
When writing a Python docstring for an execution module, add information about the module using the following field lists:
:maintainer: Thomas Hatch <[email protected], Seth House <[email protected]>
:maturity: new
:depends: python-mysqldb
:platform: all
The maintainer field is a comma-delimited list of developers who help maintain this module.
The maturity field indicates the level of quality and testing for this module. Standard labels will be determined.
The depends field is a comma-delimited list of modules that this module depends on.
The platform field is a comma-delimited list of platforms that this module is known to run on.
In Salt, Python callable objects contained within an execution module are made available
to the Salt minion for use. The only exception to this rule is a callable
object with a name starting with an underscore _
.
def foo(bar):
return bar
class baz:
def __init__(self, quo):
pass
def _foobar(baz): # Preceded with an _
return baz
cheese = {} # Not a callable Python object
Note
Some callable names also end with an underscore _
, to avoid keyword clashes
with Python keywords. When using execution modules, or state modules, with these
in them the trailing underscore should be omitted.
When writing execution modules there are many times where some of the module will work on all hosts but some functions have an external dependency, such as a service that needs to be installed or a binary that needs to be present on the system.
Instead of trying to wrap much of the code in large try/except blocks, a decorator can be used.
If the dependencies passed to the decorator don't exist, then the salt minion will remove those functions from the module on that host.
If a "fallback_function" is defined, it will replace the function instead of removing it
import logging
from salt.utils.decorators import depends
log = logging.getLogger(__name__)
try:
import dependency_that_sometimes_exists
except ImportError as e:
log.trace('Failed to import dependency_that_sometimes_exists: {0}'.format(e))
@depends('dependency_that_sometimes_exists')
def foo():
'''
Function with a dependency on the "dependency_that_sometimes_exists" module,
if the "dependency_that_sometimes_exists" is missing this function will not exist
'''
return True
def _fallback():
'''
Fallback function for the depends decorator to replace a function with
'''
return '"dependency_that_sometimes_exists" needs to be installed for this function to exist'
@depends('dependency_that_sometimes_exists', fallback_function=_fallback)
def foo():
'''
Function with a dependency on the "dependency_that_sometimes_exists" module.
If the "dependency_that_sometimes_exists" is missing this function will be
replaced with "_fallback"
'''
return True