1
0

writing.rst 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531
  1. .. _state-modules:
  2. =============
  3. State Modules
  4. =============
  5. State Modules are the components that map to actual enforcement and management
  6. of Salt states.
  7. .. _writing-state-modules:
  8. States are Easy to Write!
  9. =========================
  10. State Modules should be easy to write and straightforward. The information
  11. passed to the SLS data structures will map directly to the states modules.
  12. Mapping the information from the SLS data is simple, this example should
  13. illustrate:
  14. .. code-block:: yaml
  15. /etc/salt/master: # maps to "name", unless a "name" argument is specified below
  16. file.managed: # maps to <filename>.<function> - e.g. "managed" in https://github.com/saltstack/salt/tree/|repo_primary_branch|/salt/states/file.py
  17. - user: root # one of many options passed to the manage function
  18. - group: root
  19. - mode: 644
  20. - source: salt://salt/master
  21. Therefore this SLS data can be directly linked to a module, function, and
  22. arguments passed to that function.
  23. This does issue the burden, that function names, state names and function
  24. arguments should be very human readable inside state modules, since they
  25. directly define the user interface.
  26. .. admonition:: Keyword Arguments
  27. Salt passes a number of keyword arguments to states when rendering them,
  28. including the environment, a unique identifier for the state, and more.
  29. Additionally, keep in mind that the requisites for a state are part of the
  30. keyword arguments. Therefore, if you need to iterate through the keyword
  31. arguments in a state, these must be considered and handled appropriately.
  32. One such example is in the :mod:`pkgrepo.managed
  33. <salt.states.pkgrepo.managed>` state, which needs to be able to handle
  34. arbitrary keyword arguments and pass them to module execution functions.
  35. An example of how these keyword arguments can be handled can be found
  36. here_.
  37. .. _here: https://github.com/saltstack/salt/blob/v0.16.2/salt/states/pkgrepo.py#L163-183
  38. Best Practices
  39. ==============
  40. A well-written state function will follow these steps:
  41. .. note::
  42. This is an extremely simplified example. Feel free to browse the `source
  43. code`_ for Salt's state modules to see other examples.
  44. .. _`source code`: https://github.com/saltstack/salt/tree/|repo_primary_branch|/salt/states
  45. 1. Set up the return dictionary and perform any necessary input validation
  46. (type checking, looking for use of mutually-exclusive arguments, etc.).
  47. .. code-block:: python
  48. def myfunc():
  49. ret = {"name": name, "result": False, "changes": {}, "comment": ""}
  50. if foo and bar:
  51. ret["comment"] = "Only one of foo and bar is permitted"
  52. return ret
  53. 2. Check if changes need to be made. This is best done with an
  54. information-gathering function in an accompanying :ref:`execution module
  55. <writing-execution-modules>`. The state should be able to use the return
  56. from this function to tell whether or not the minion is already in the
  57. desired state.
  58. .. code-block:: python
  59. result = __salt__["modname.check"](name)
  60. 3. If step 2 found that the minion is already in the desired state, then exit
  61. immediately with a ``True`` result and without making any changes.
  62. .. code-block:: python
  63. def myfunc():
  64. if result:
  65. ret["result"] = True
  66. ret["comment"] = "{0} is already installed".format(name)
  67. return ret
  68. 4. If step 2 found that changes *do* need to be made, then check to see if the
  69. state was being run in test mode (i.e. with ``test=True``). If so, then exit
  70. with a ``None`` result, a relevant comment, and (if possible) a ``changes``
  71. entry describing what changes would be made.
  72. .. code-block:: python
  73. def myfunc():
  74. if __opts__["test"]:
  75. ret["result"] = None
  76. ret["comment"] = "{0} would be installed".format(name)
  77. ret["changes"] = result
  78. return ret
  79. 5. Make the desired changes. This should again be done using a function from an
  80. accompanying execution module. If the result of that function is enough to
  81. tell you whether or not an error occurred, then you can exit with a
  82. ``False`` result and a relevant comment to explain what happened.
  83. .. code-block:: python
  84. result = __salt__["modname.install"](name)
  85. 6. Perform the same check from step 2 again to confirm whether or not the
  86. minion is in the desired state. Just as in step 2, this function should be
  87. able to tell you by its return data whether or not changes need to be made.
  88. .. code-block:: python
  89. ret["changes"] = __salt__["modname.check"](name)
  90. As you can see here, we are setting the ``changes`` key in the return
  91. dictionary to the result of the ``modname.check`` function (just as we did
  92. in step 4). The assumption here is that the information-gathering function
  93. will return a dictionary explaining what changes need to be made. This may
  94. or may not fit your use case.
  95. 7. Set the return data and return!
  96. .. code-block:: python
  97. def myfunc():
  98. if ret["changes"]:
  99. ret["comment"] = "{0} failed to install".format(name)
  100. else:
  101. ret["result"] = True
  102. ret["comment"] = "{0} was installed".format(name)
  103. return ret
  104. Using Custom State Modules
  105. ==========================
  106. Before the state module can be used, it must be distributed to minions. This
  107. can be done by placing them into ``salt://_states/``. They can then be
  108. distributed manually to minions by running :mod:`saltutil.sync_states
  109. <salt.modules.saltutil.sync_states>` or :mod:`saltutil.sync_all
  110. <salt.modules.saltutil.sync_all>`. Alternatively, when running a
  111. :ref:`highstate <running-highstate>` custom types will automatically be synced.
  112. NOTE: Writing state modules with hyphens in the filename will cause issues
  113. with !pyobjects routines. Best practice to stick to underscores.
  114. Any custom states which have been synced to a minion, that are named the same
  115. as one of Salt's default set of states, will take the place of the default
  116. state with the same name. Note that a state module's name defaults to one based
  117. on its filename (i.e. ``foo.py`` becomes state module ``foo``), but that its
  118. name can be overridden by using a :ref:`__virtual__ function
  119. <virtual-modules>`.
  120. Cross Calling Execution Modules from States
  121. ===========================================
  122. As with Execution Modules, State Modules can also make use of the ``__salt__``
  123. and ``__grains__`` data. See :ref:`cross calling execution modules
  124. <cross-calling-execution-modules>`.
  125. It is important to note that the real work of state management should not be
  126. done in the state module unless it is needed. A good example is the pkg state
  127. module. This module does not do any package management work, it just calls the
  128. pkg execution module. This makes the pkg state module completely generic, which
  129. is why there is only one pkg state module and many backend pkg execution
  130. modules.
  131. On the other hand some modules will require that the logic be placed in the
  132. state module, a good example of this is the file module. But in the vast
  133. majority of cases this is not the best approach, and writing specific
  134. execution modules to do the backend work will be the optimal solution.
  135. .. _cross-calling-state-modules:
  136. Cross Calling State Modules
  137. ===========================
  138. All of the Salt state modules are available to each other and state modules can call
  139. functions available in other state modules.
  140. The variable ``__states__`` is packed into the modules after they are loaded into
  141. the Salt minion.
  142. The ``__states__`` variable is a :ref:`Python dictionary <python:typesmapping>`
  143. containing all of the state modules. Dictionary keys are strings representing
  144. the names of the modules and the values are the functions themselves.
  145. Salt state modules can be cross-called by accessing the value in the
  146. ``__states__`` dict:
  147. .. code-block:: python
  148. ret = __states__["file.managed"](name="/tmp/myfile", source="salt://myfile")
  149. This code will call the `managed` function in the :mod:`file
  150. <salt.states.file>` state module and pass the arguments ``name`` and ``source``
  151. to it.
  152. .. _state-return-data:
  153. Return Data
  154. ===========
  155. A State Module must return a dict containing the following keys/values:
  156. - **name:** The same value passed to the state as "name".
  157. - **changes:** A dict describing the changes made. Each thing changed should
  158. be a key, with its value being another dict with keys called "old" and "new"
  159. containing the old/new values. For example, the pkg state's **changes** dict
  160. has one key for each package changed, with the "old" and "new" keys in its
  161. sub-dict containing the old and new versions of the package. For example,
  162. the final changes dictionary for this scenario would look something like this:
  163. .. code-block:: python
  164. ret["changes"].update({"my_pkg_name": {"old": "", "new": "my_pkg_name-1.0"}})
  165. - **result:** A tristate value. ``True`` if the action was successful,
  166. ``False`` if it was not, or ``None`` if the state was run in test mode,
  167. ``test=True``, and changes would have been made if the state was not run in
  168. test mode.
  169. +--------------------+-----------+------------------------+
  170. | | live mode | test mode |
  171. +====================+===========+========================+
  172. | no changes | ``True`` | ``True`` |
  173. +--------------------+-----------+------------------------+
  174. | successful changes | ``True`` | ``None`` |
  175. +--------------------+-----------+------------------------+
  176. | failed changes | ``False`` | ``False`` or ``None`` |
  177. +--------------------+-----------+------------------------+
  178. .. note::
  179. Test mode does not predict if the changes will be successful or not,
  180. and hence the result for pending changes is usually ``None``.
  181. However, if a state is going to fail and this can be determined
  182. in test mode without applying the change, ``False`` can be returned.
  183. - **comment:** A list of strings or a single string summarizing the result.
  184. Note that support for lists of strings is available as of Salt 2018.3.0.
  185. Lists of strings will be joined with newlines to form the final comment;
  186. this is useful to allow multiple comments from subparts of a state.
  187. Prefer to keep line lengths short (use multiple lines as needed),
  188. and end with punctuation (e.g. a period) to delimit multiple comments.
  189. .. note::
  190. States should not return data which cannot be serialized such as frozensets.
  191. Sub State Runs
  192. --------------
  193. Some states can return multiple state runs from an external engine.
  194. State modules that extend tools like Puppet, Chef, Ansible, and idem can run multiple external
  195. states and then return their results individually in the "sub_state_run" portion of their return
  196. as long as their individual state runs are formatted like salt states with low and high data.
  197. For example, the idem state module can execute multiple idem states
  198. via it's runtime and report the status of all those runs by attaching them to "sub_state_run" in it's state return.
  199. These sub_state_runs will be formatted and printed alongside other salt states.
  200. Example:
  201. .. code-block:: python
  202. state_return = {
  203. "name": None, # The parent state name
  204. "result": None, # The overall status of the external state engine run
  205. "comment": None, # Comments on the overall external state engine run
  206. "changes": {}, # An empty dictionary, each sub state run has it's own changes to report
  207. "sub_state_run": [
  208. {
  209. "changes": {}, # A dictionary describing the changes made in the external state run
  210. "result": None, # The external state run name
  211. "comment": None, # Comment on the external state run
  212. "duration": None, # Optional, the duration in seconds of the external state run
  213. "start_time": None, # Optional, the timestamp of the external state run's start time
  214. "low": {
  215. "name": None, # The name of the state from the external state run
  216. "state": None, # Name of the external state run
  217. "__id__": None, # ID of the external state run
  218. "fun": None, # The Function name from the external state run
  219. },
  220. }
  221. ],
  222. }
  223. Test State
  224. ==========
  225. All states should check for and support ``test`` being passed in the options.
  226. This will return data about what changes would occur if the state were actually
  227. run. An example of such a check could look like this:
  228. .. code-block:: python
  229. def myfunc():
  230. # Return comment of changes if test.
  231. if __opts__["test"]:
  232. ret["result"] = None
  233. ret["comment"] = "State Foo will execute with param {0}".format(bar)
  234. return ret
  235. Make sure to test and return before performing any real actions on the minion.
  236. .. note::
  237. Be sure to refer to the ``result`` table listed above and displaying any
  238. possible changes when writing support for ``test``. Looking for changes in
  239. a state is essential to ``test=true`` functionality. If a state is predicted
  240. to have no changes when ``test=true`` (or ``test: true`` in a config file)
  241. is used, then the result of the final state **should not** be ``None``.
  242. Watcher Function
  243. ================
  244. If the state being written should support the watch requisite then a watcher
  245. function needs to be declared. The watcher function is called whenever the
  246. watch requisite is invoked and should be generic to the behavior of the state
  247. itself.
  248. The watcher function should accept all of the options that the normal state
  249. functions accept (as they will be passed into the watcher function).
  250. A watcher function typically is used to execute state specific reactive
  251. behavior, for instance, the watcher for the service module restarts the
  252. named service and makes it useful for the watcher to make the service
  253. react to changes in the environment.
  254. The watcher function also needs to return the same data that a normal state
  255. function returns.
  256. Mod_init Interface
  257. ==================
  258. Some states need to execute something only once to ensure that an environment
  259. has been set up, or certain conditions global to the state behavior can be
  260. predefined. This is the realm of the mod_init interface.
  261. A state module can have a function called **mod_init** which executes when the
  262. first state of this type is called. This interface was created primarily to
  263. improve the pkg state. When packages are installed the package metadata needs
  264. to be refreshed, but refreshing the package metadata every time a package is
  265. installed is wasteful. The mod_init function for the pkg state sets a flag down
  266. so that the first, and only the first, package installation attempt will refresh
  267. the package database (the package database can of course be manually called to
  268. refresh via the ``refresh`` option in the pkg state).
  269. The mod_init function must accept the **Low State Data** for the given
  270. executing state as an argument. The low state data is a dict and can be seen by
  271. executing the state.show_lowstate function. Then the mod_init function must
  272. return a bool. If the return value is True, then the mod_init function will not
  273. be executed again, meaning that the needed behavior has been set up. Otherwise,
  274. if the mod_init function returns False, then the function will be called the
  275. next time.
  276. A good example of the mod_init function is found in the pkg state module:
  277. .. code-block:: python
  278. def mod_init(low):
  279. """
  280. Refresh the package database here so that it only needs to happen once
  281. """
  282. if low["fun"] == "installed" or low["fun"] == "latest":
  283. rtag = __gen_rtag()
  284. if not os.path.exists(rtag):
  285. open(rtag, "w+").write("")
  286. return True
  287. else:
  288. return False
  289. The mod_init function in the pkg state accepts the low state data as ``low``
  290. and then checks to see if the function being called is going to install
  291. packages, if the function is not going to install packages then there is no
  292. need to refresh the package database. Therefore if the package database is
  293. prepared to refresh, then return True and the mod_init will not be called
  294. the next time a pkg state is evaluated, otherwise return False and the mod_init
  295. will be called next time a pkg state is evaluated.
  296. Log Output
  297. ==========
  298. You can call the logger from custom modules to write messages to the minion
  299. logs. The following code snippet demonstrates writing log messages:
  300. .. code-block:: python
  301. import logging
  302. log = logging.getLogger(__name__)
  303. log.info("Here is Some Information")
  304. log.warning("You Should Not Do That")
  305. log.error("It Is Busted")
  306. Strings and Unicode
  307. ===================
  308. A state module author should always assume that strings fed to the module
  309. have already decoded from strings into Unicode. In Python 2, these will
  310. be of type 'Unicode' and in Python 3 they will be of type ``str``. Calling
  311. from a state to other Salt sub-systems, such as execution modules should
  312. pass Unicode (or bytes if passing binary data). In the rare event that a state needs to write directly
  313. to disk, Unicode should be encoded to a string immediately before writing
  314. to disk. An author may use ``__salt_system_encoding__`` to learn what the
  315. encoding type of the system is. For example,
  316. `'my_string'.encode(__salt_system_encoding__')`.
  317. Full State Module Example
  318. =========================
  319. The following is a simplistic example of a full state module and function.
  320. Remember to call out to execution modules to perform all the real work. The
  321. state module should only perform "before" and "after" checks.
  322. 1. Make a custom state module by putting the code into a file at the following
  323. path: **/srv/salt/_states/my_custom_state.py**.
  324. 2. Distribute the custom state module to the minions:
  325. .. code-block:: bash
  326. salt '*' saltutil.sync_states
  327. 3. Write a new state to use the custom state by making a new state file, for
  328. instance **/srv/salt/my_custom_state.sls**.
  329. 4. Add the following SLS configuration to the file created in Step 3:
  330. .. code-block:: yaml
  331. human_friendly_state_id: # An arbitrary state ID declaration.
  332. my_custom_state: # The custom state module name.
  333. - enforce_custom_thing # The function in the custom state module.
  334. - name: a_value # Maps to the ``name`` parameter in the custom function.
  335. - foo: Foo # Specify the required ``foo`` parameter.
  336. - bar: False # Override the default value for the ``bar`` parameter.
  337. Example state module
  338. --------------------
  339. .. code-block:: python
  340. import salt.exceptions
  341. def enforce_custom_thing(name, foo, bar=True):
  342. """
  343. Enforce the state of a custom thing
  344. This state module does a custom thing. It calls out to the execution module
  345. ``my_custom_module`` in order to check the current system and perform any
  346. needed changes.
  347. name
  348. The thing to do something to
  349. foo
  350. A required argument
  351. bar : True
  352. An argument with a default value
  353. """
  354. ret = {
  355. "name": name,
  356. "changes": {},
  357. "result": False,
  358. "comment": "",
  359. }
  360. # Start with basic error-checking. Do all the passed parameters make sense
  361. # and agree with each-other?
  362. if bar == True and foo.startswith("Foo"):
  363. raise salt.exceptions.SaltInvocationError(
  364. 'Argument "foo" cannot start with "Foo" if argument "bar" is True.'
  365. )
  366. # Check the current state of the system. Does anything need to change?
  367. current_state = __salt__["my_custom_module.current_state"](name)
  368. if current_state == foo:
  369. ret["result"] = True
  370. ret["comment"] = "System already in the correct state"
  371. return ret
  372. # The state of the system does need to be changed. Check if we're running
  373. # in ``test=true`` mode.
  374. if __opts__["test"] == True:
  375. ret["comment"] = 'The state of "{0}" will be changed.'.format(name)
  376. ret["changes"] = {
  377. "old": current_state,
  378. "new": "Description, diff, whatever of the new state",
  379. }
  380. # Return ``None`` when running with ``test=true``.
  381. ret["result"] = None
  382. return ret
  383. # Finally, make the actual change and return the result.
  384. new_state = __salt__["my_custom_module.change_state"](name, foo)
  385. ret["comment"] = 'The state of "{0}" was changed!'.format(name)
  386. ret["changes"] = {
  387. "old": current_state,
  388. "new": new_state,
  389. }
  390. ret["result"] = True
  391. return ret