1
0

index.rst 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740
  1. .. _reactor:
  2. .. index:: ! Reactor, Salt Reactor
  3. seealso: Event; Reactor
  4. ==============
  5. Reactor System
  6. ==============
  7. Salt's Reactor system gives Salt the ability to trigger actions in response to
  8. an event. It is a simple interface to watching Salt's event bus for event tags
  9. that match a given pattern and then running one or more commands in response.
  10. This system binds sls files to event tags on the master. These sls files then
  11. define reactions. This means that the reactor system has two parts. First, the
  12. reactor option needs to be set in the master configuration file. The reactor
  13. option allows for event tags to be associated with sls reaction files. Second,
  14. these reaction files use highdata (like the state system) to define reactions
  15. to be executed.
  16. Event System
  17. ============
  18. A basic understanding of the event system is required to understand reactors.
  19. The event system is a local ZeroMQ PUB interface which fires salt events. This
  20. event bus is an open system used for sending information notifying Salt and
  21. other systems about operations.
  22. The event system fires events with a very specific criteria. Every event has a
  23. **tag**. Event tags allow for fast top-level filtering of events. In addition
  24. to the tag, each event has a data structure. This data structure is a
  25. dictionary, which contains information about the event.
  26. .. _reactor-mapping-events:
  27. Mapping Events to Reactor SLS Files
  28. ===================================
  29. Reactor SLS files and event tags are associated in the master config file.
  30. By default this is /etc/salt/master, or /etc/salt/master.d/reactor.conf.
  31. .. versionadded:: 2014.7.0
  32. Added Reactor support for ``salt://`` file paths.
  33. In the master config section 'reactor:' is a list of event tags to be matched
  34. and each event tag has a list of reactor SLS files to be run.
  35. .. code-block:: yaml
  36. reactor: # Master config section "reactor"
  37. - 'salt/minion/*/start': # Match tag "salt/minion/*/start"
  38. - /srv/reactor/start.sls # Things to do when a minion starts
  39. - /srv/reactor/monitor.sls # Other things to do
  40. - 'salt/cloud/*/destroyed': # Globs can be used to match tags
  41. - /srv/reactor/destroy/*.sls # Globs can be used to match file names
  42. - 'myco/custom/event/tag': # React to custom event tags
  43. - salt://reactor/mycustom.sls # Reactor files can come from the salt fileserver
  44. .. note::
  45. In the above example, ``salt://reactor/mycustom.sls`` refers to the
  46. ``base`` environment. To pull this file from a different environment, use
  47. the :ref:`querystring syntax <querystring-syntax>` (e.g.
  48. ``salt://reactor/mycustom.sls?saltenv=reactor``).
  49. Reactor SLS files are similar to State and Pillar SLS files. They are by
  50. default YAML + Jinja templates and are passed familiar context variables.
  51. Click :ref:`here <reactor-jinja-context>` for more detailed information on the
  52. variables available in Jinja templating.
  53. Here is the SLS for a simple reaction:
  54. .. code-block:: jinja
  55. {% if data['id'] == 'mysql1' %}
  56. highstate_run:
  57. local.state.apply:
  58. - tgt: mysql1
  59. {% endif %}
  60. This simple reactor file uses Jinja to further refine the reaction to be made.
  61. If the ``id`` in the event data is ``mysql1`` (in other words, if the name of
  62. the minion is ``mysql1``) then the following reaction is defined. The same
  63. data structure and compiler used for the state system is used for the reactor
  64. system. The only difference is that the data is matched up to the salt command
  65. API and the runner system. In this example, a command is published to the
  66. ``mysql1`` minion with a function of :py:func:`state.apply
  67. <salt.modules.state.apply_>`, which performs a :ref:`highstate
  68. <running-highstate>`. Similarly, a runner can be called:
  69. .. code-block:: jinja
  70. {% if data['data']['custom_var'] == 'runit' %}
  71. call_runit_orch:
  72. runner.state.orchestrate:
  73. - args:
  74. - mods: orchestrate.runit
  75. {% endif %}
  76. This example will execute the state.orchestrate runner and intiate an execution
  77. of the ``runit`` orchestrator located at ``/srv/salt/orchestrate/runit.sls``.
  78. Types of Reactions
  79. ==================
  80. ============================== ==================================================================================
  81. Name Description
  82. ============================== ==================================================================================
  83. :ref:`local <reactor-local>` Runs a :ref:`remote-execution function <all-salt.modules>` on targeted minions
  84. :ref:`runner <reactor-runner>` Executes a :ref:`runner function <all-salt.runners>`
  85. :ref:`wheel <reactor-wheel>` Executes a :ref:`wheel function <all-salt.wheel>` on the master
  86. :ref:`caller <reactor-caller>` Runs a :ref:`remote-execution function <all-salt.modules>` on a masterless minion
  87. ============================== ==================================================================================
  88. .. note::
  89. The ``local`` and ``caller`` reaction types will likely be renamed in a
  90. future release. These reaction types were named after Salt's internal
  91. client interfaces, and are not intuitively named. Both ``local`` and
  92. ``caller`` will continue to work in Reactor SLS files, however.
  93. Where to Put Reactor SLS Files
  94. ==============================
  95. Reactor SLS files can come both from files local to the master, and from any of
  96. backends enabled via the :conf_master:`fileserver_backend` config option. Files
  97. placed in the Salt fileserver can be referenced using a ``salt://`` URL, just
  98. like they can in State SLS files.
  99. It is recommended to place reactor and orchestrator SLS files in their own
  100. uniquely-named subdirectories such as ``orch/``, ``orchestrate/``, ``react/``,
  101. ``reactor/``, etc., to keep them organized.
  102. .. _reactor-sls:
  103. Writing Reactor SLS
  104. ===================
  105. The different reaction types were developed separately and have historically
  106. had different methods for passing arguments. For the 2017.7.2 release a new,
  107. unified configuration schema has been introduced, which applies to all reaction
  108. types.
  109. The old config schema will continue to be supported, and there is no plan to
  110. deprecate it at this time.
  111. .. _reactor-local:
  112. Local Reactions
  113. ---------------
  114. A ``local`` reaction runs a :ref:`remote-execution function <all-salt.modules>`
  115. on the targeted minions.
  116. The old config schema required the positional and keyword arguments to be
  117. manually separated by the user under ``arg`` and ``kwarg`` parameters. However,
  118. this is not very user-friendly, as it forces the user to distinguish which type
  119. of argument is which, and make sure that positional arguments are ordered
  120. properly. Therefore, the new config schema is recommended if the master is
  121. running a supported release.
  122. The below two examples are equivalent:
  123. +---------------------------------+-----------------------------+
  124. | Supported in 2017.7.2 and later | Supported in all releases |
  125. +=================================+=============================+
  126. | :: | :: |
  127. | | |
  128. | install_zsh: | install_zsh: |
  129. | local.state.single: | local.state.single: |
  130. | - tgt: 'kernel:Linux' | - tgt: 'kernel:Linux' |
  131. | - tgt_type: grain | - tgt_type: grain |
  132. | - args: | - arg: |
  133. | - fun: pkg.installed | - pkg.installed |
  134. | - name: zsh | - zsh |
  135. | - fromrepo: updates | - kwarg: |
  136. | | fromrepo: updates |
  137. +---------------------------------+-----------------------------+
  138. This reaction would be equivalent to running the following Salt command:
  139. .. code-block:: bash
  140. salt -G 'kernel:Linux' state.single pkg.installed name=zsh fromrepo=updates
  141. .. note::
  142. Any other parameters in the :py:meth:`LocalClient().cmd_async()
  143. <salt.client.LocalClient.cmd_async>` method can be passed at the same
  144. indentation level as ``tgt``.
  145. .. note::
  146. ``tgt_type`` is only required when the target expression defined in ``tgt``
  147. uses a :ref:`target type <targeting>` other than a minion ID glob.
  148. The ``tgt_type`` argument was named ``expr_form`` in releases prior to
  149. 2017.7.0.
  150. .. _reactor-runner:
  151. Runner Reactions
  152. ----------------
  153. Runner reactions execute :ref:`runner functions <all-salt.runners>` locally on
  154. the master.
  155. The old config schema called for passing arguments to the reaction directly
  156. under the name of the runner function. However, this can cause unpredictable
  157. interactions with the Reactor system's internal arguments. It is also possible
  158. to pass positional and keyword arguments under ``arg`` and ``kwarg`` like above
  159. in :ref:`local reactions <reactor-local>`, but as noted above this is not very
  160. user-friendly. Therefore, the new config schema is recommended if the master
  161. is running a supported release.
  162. The below two examples are equivalent:
  163. +-------------------------------------------------+-------------------------------------------------+
  164. | Supported in 2017.7.2 and later | Supported in all releases |
  165. +=================================================+=================================================+
  166. | :: | :: |
  167. | | |
  168. | deploy_app: | deploy_app: |
  169. | runner.state.orchestrate: | runner.state.orchestrate: |
  170. | - args: | - mods: orchestrate.deploy_app |
  171. | - mods: orchestrate.deploy_app | - kwarg: |
  172. | - pillar: | pillar: |
  173. | event_tag: {{ tag }} | event_tag: {{ tag }} |
  174. | event_data: {{ data['data']|json }} | event_data: {{ data['data']|json }} |
  175. +-------------------------------------------------+-------------------------------------------------+
  176. Assuming that the event tag is ``foo``, and the data passed to the event is
  177. ``{'bar': 'baz'}``, then this reaction is equivalent to running the following
  178. Salt command:
  179. .. code-block:: bash
  180. salt-run state.orchestrate mods=orchestrate.deploy_app pillar='{"event_tag": "foo", "event_data": {"bar": "baz"}}'
  181. .. _reactor-wheel:
  182. Wheel Reactions
  183. ---------------
  184. Wheel reactions run :ref:`wheel functions <all-salt.wheel>` locally on the
  185. master.
  186. Like :ref:`runner reactions <reactor-runner>`, the old config schema called for
  187. wheel reactions to have arguments passed directly under the name of the
  188. :ref:`wheel function <all-salt.wheel>` (or in ``arg`` or ``kwarg`` parameters).
  189. The below two examples are equivalent:
  190. +-----------------------------------+---------------------------------+
  191. | Supported in 2017.7.2 and later | Supported in all releases |
  192. +===================================+=================================+
  193. | :: | :: |
  194. | | |
  195. | remove_key: | remove_key: |
  196. | wheel.key.delete: | wheel.key.delete: |
  197. | - args: | - match: {{ data['id'] }} |
  198. | - match: {{ data['id'] }} | |
  199. +-----------------------------------+---------------------------------+
  200. .. _reactor-caller:
  201. Caller Reactions
  202. ----------------
  203. Caller reactions run :ref:`remote-execution functions <all-salt.modules>` on a
  204. minion daemon's Reactor system. To run a Reactor on the minion, it is necessary
  205. to configure the :mod:`Reactor Engine <salt.engines.reactor>` in the minion
  206. config file, and then setup your watched events in a ``reactor`` section in the
  207. minion config file as well.
  208. .. note:: Masterless Minions use this Reactor
  209. This is the only way to run the Reactor if you use masterless minions.
  210. Both the old and new config schemas involve passing arguments under an ``args``
  211. parameter. However, the old config schema only supports positional arguments.
  212. Therefore, the new config schema is recommended if the masterless minion is
  213. running a supported release.
  214. The below two examples are equivalent:
  215. +---------------------------------+---------------------------+
  216. | Supported in 2017.7.2 and later | Supported in all releases |
  217. +=================================+===========================+
  218. | :: | :: |
  219. | | |
  220. | touch_file: | touch_file: |
  221. | caller.file.touch: | caller.file.touch: |
  222. | - args: | - args: |
  223. | - name: /tmp/foo | - /tmp/foo |
  224. +---------------------------------+---------------------------+
  225. This reaction is equivalent to running the following Salt command:
  226. .. code-block:: bash
  227. salt-call file.touch name=/tmp/foo
  228. Best Practices for Writing Reactor SLS Files
  229. ============================================
  230. The Reactor works as follows:
  231. 1. The Salt Reactor watches Salt's event bus for new events.
  232. 2. Each event's tag is matched against the list of event tags configured under
  233. the :conf_master:`reactor` section in the Salt Master config.
  234. 3. The SLS files for any matches are rendered into a data structure that
  235. represents one or more function calls.
  236. 4. That data structure is given to a pool of worker threads for execution.
  237. Matching and rendering Reactor SLS files is done sequentially in a single
  238. process. For that reason, reactor SLS files should contain few individual
  239. reactions (one, if at all possible). Also, keep in mind that reactions are
  240. fired asynchronously (with the exception of :ref:`caller <reactor-caller>`) and
  241. do *not* support :ref:`requisites <requisites>`.
  242. Complex Jinja templating that calls out to slow :ref:`remote-execution
  243. <all-salt.modules>` or :ref:`runner <all-salt.runners>` functions slows down
  244. the rendering and causes other reactions to pile up behind the current one. The
  245. worker pool is designed to handle complex and long-running processes like
  246. :ref:`orchestration <orchestrate-runner>` jobs.
  247. Therefore, when complex tasks are in order, :ref:`orchestration
  248. <orchestrate-runner>` is a natural fit. Orchestration SLS files can be more
  249. complex, and use requisites. Performing a complex task using orchestration lets
  250. the Reactor system fire off the orchestration job and proceed with processing
  251. other reactions.
  252. .. _reactor-jinja-context:
  253. Jinja Context
  254. =============
  255. Reactor SLS files only have access to a minimal Jinja context. ``grains`` and
  256. ``pillar`` are *not* available. The ``salt`` object is available for calling
  257. :ref:`remote-execution <all-salt.modules>` or :ref:`runner <all-salt.runners>`
  258. functions, but it should be used sparingly and only for quick tasks for the
  259. reasons mentioned above.
  260. In addition to the ``salt`` object, the following variables are available in
  261. the Jinja context:
  262. - ``tag`` - the tag from the event that triggered execution of the Reactor SLS
  263. file
  264. - ``data`` - the event's data dictionary
  265. The ``data`` dict will contain an ``id`` key containing the minion ID, if the
  266. event was fired from a minion, and a ``data`` key containing the data passed to
  267. the event.
  268. Advanced State System Capabilities
  269. ==================================
  270. Reactor SLS files, by design, do not support :ref:`requisites <requisites>`,
  271. ordering, ``onlyif``/``unless`` conditionals and most other powerful constructs
  272. from Salt's State system.
  273. Complex Master-side operations are best performed by Salt's Orchestrate system
  274. so using the Reactor to kick off an Orchestrate run is a very common pairing.
  275. For example:
  276. .. code-block:: jinja
  277. # /etc/salt/master.d/reactor.conf
  278. # A custom event containing: {"foo": "Foo!", "bar: "bar*", "baz": "Baz!"}
  279. reactor:
  280. - my/custom/event:
  281. - /srv/reactor/some_event.sls
  282. .. code-block:: jinja
  283. # /srv/reactor/some_event.sls
  284. invoke_orchestrate_file:
  285. runner.state.orchestrate:
  286. - args:
  287. - mods: orchestrate.do_complex_thing
  288. - pillar:
  289. event_tag: {{ tag }}
  290. event_data: {{ data|json }}
  291. .. code-block:: jinja
  292. # /srv/salt/orchestrate/do_complex_thing.sls
  293. {% set tag = salt.pillar.get('event_tag') %}
  294. {% set data = salt.pillar.get('event_data') %}
  295. # Pass data from the event to a custom runner function.
  296. # The function expects a 'foo' argument.
  297. do_first_thing:
  298. salt.runner:
  299. - name: custom_runner.custom_function
  300. - foo: {{ data.foo }}
  301. # Wait for the runner to finish then send an execution to minions.
  302. # Forward some data from the event down to the minion's state run.
  303. do_second_thing:
  304. salt.state:
  305. - tgt: {{ data.bar }}
  306. - sls:
  307. - do_thing_on_minion
  308. - kwarg:
  309. pillar:
  310. baz: {{ data.baz }}
  311. - require:
  312. - salt: do_first_thing
  313. .. _beacons-and-reactors:
  314. Beacons and Reactors
  315. ====================
  316. An event initiated by a beacon, when it arrives at the master will be wrapped
  317. inside a second event, such that the data object containing the beacon
  318. information will be ``data['data']``, rather than ``data``.
  319. For example, to access the ``id`` field of the beacon event in a reactor file,
  320. you will need to reference ``{{ data['data']['id'] }}`` rather than ``{{
  321. data['id'] }}`` as for events initiated directly on the event bus.
  322. Similarly, the data dictionary attached to the event would be located in
  323. ``{{ data['data']['data'] }}`` instead of ``{{ data['data'] }}``.
  324. See the :ref:`beacon documentation <beacon-example>` for examples.
  325. Manually Firing an Event
  326. ========================
  327. From the Master
  328. ---------------
  329. Use the :py:func:`event.send <salt.runners.event.send>` runner:
  330. .. code-block:: bash
  331. salt-run event.send foo '{orchestrate: refresh}'
  332. From the Minion
  333. ---------------
  334. To fire an event to the master from a minion, call :py:func:`event.send
  335. <salt.modules.event.send>`:
  336. .. code-block:: bash
  337. salt-call event.send foo '{orchestrate: refresh}'
  338. To fire an event to the minion's local event bus, call :py:func:`event.fire
  339. <salt.modules.event.fire>`:
  340. .. code-block:: bash
  341. salt-call event.fire '{orchestrate: refresh}' foo
  342. Referencing Data Passed in Events
  343. ---------------------------------
  344. Assuming any of the above examples, any reactor SLS files triggered by watching
  345. the event tag ``foo`` will execute with ``{{ data['data']['orchestrate'] }}``
  346. equal to ``'refresh'``.
  347. Getting Information About Events
  348. ================================
  349. The best way to see exactly what events have been fired and what data is
  350. available in each event is to use the :py:func:`state.event runner
  351. <salt.runners.state.event>`.
  352. .. seealso:: :ref:`Common Salt Events <event-master_events>`
  353. Example usage:
  354. .. code-block:: bash
  355. salt-run state.event pretty=True
  356. Example output:
  357. .. code-block:: text
  358. salt/job/20150213001905721678/new {
  359. "_stamp": "2015-02-13T00:19:05.724583",
  360. "arg": [],
  361. "fun": "test.ping",
  362. "jid": "20150213001905721678",
  363. "minions": [
  364. "jerry"
  365. ],
  366. "tgt": "*",
  367. "tgt_type": "glob",
  368. "user": "root"
  369. }
  370. salt/job/20150213001910749506/ret/jerry {
  371. "_stamp": "2015-02-13T00:19:11.136730",
  372. "cmd": "_return",
  373. "fun": "saltutil.find_job",
  374. "fun_args": [
  375. "20150213001905721678"
  376. ],
  377. "id": "jerry",
  378. "jid": "20150213001910749506",
  379. "retcode": 0,
  380. "return": {},
  381. "success": true
  382. }
  383. Debugging the Reactor
  384. =====================
  385. The best window into the Reactor is to run the master in the foreground with
  386. debug logging enabled. The output will include when the master sees the event,
  387. what the master does in response to that event, and it will also include the
  388. rendered SLS file (or any errors generated while rendering the SLS file).
  389. 1. Stop the master.
  390. 2. Start the master manually:
  391. .. code-block:: bash
  392. salt-master -l debug
  393. 3. Look for log entries in the form:
  394. .. code-block:: text
  395. [DEBUG ] Gathering reactors for tag foo/bar
  396. [DEBUG ] Compiling reactions for tag foo/bar
  397. [DEBUG ] Rendered data from file: /path/to/the/reactor_file.sls:
  398. <... Rendered output appears here. ...>
  399. The rendered output is the result of the Jinja parsing and is a good way to
  400. view the result of referencing Jinja variables. If the result is empty then
  401. Jinja produced an empty result and the Reactor will ignore it.
  402. Passing Event Data to Minions or Orchestration as Pillar
  403. --------------------------------------------------------
  404. An interesting trick to pass data from the Reactor SLS file to
  405. :py:func:`state.apply <salt.modules.state.apply_>` is to pass it as inline
  406. Pillar data since both functions take a keyword argument named ``pillar``.
  407. The following example uses Salt's Reactor to listen for the event that is fired
  408. when the key for a new minion is accepted on the master using ``salt-key``.
  409. :file:`/etc/salt/master.d/reactor.conf`:
  410. .. code-block:: yaml
  411. reactor:
  412. - 'salt/key':
  413. - /srv/salt/haproxy/react_new_minion.sls
  414. The Reactor then fires a ::py:func:`state.apply <salt.modules.state.apply_>`
  415. command targeted to the HAProxy servers and passes the ID of the new minion
  416. from the event to the state file via inline Pillar.
  417. :file:`/srv/salt/haproxy/react_new_minion.sls`:
  418. .. code-block:: jinja
  419. {% if data['act'] == 'accept' and data['id'].startswith('web') %}
  420. add_new_minion_to_pool:
  421. local.state.apply:
  422. - tgt: 'haproxy*'
  423. - args:
  424. - mods: haproxy.refresh_pool
  425. - pillar:
  426. new_minion: {{ data['id'] }}
  427. {% endif %}
  428. The above command is equivalent to the following command at the CLI:
  429. .. code-block:: bash
  430. salt 'haproxy*' state.apply haproxy.refresh_pool pillar='{new_minion: minionid}'
  431. This works with Orchestrate files as well:
  432. .. code-block:: yaml
  433. call_some_orchestrate_file:
  434. runner.state.orchestrate:
  435. - args:
  436. - mods: orchestrate.some_orchestrate_file
  437. - pillar:
  438. stuff: things
  439. Which is equivalent to the following command at the CLI:
  440. .. code-block:: bash
  441. salt-run state.orchestrate orchestrate.some_orchestrate_file pillar='{stuff: things}'
  442. Finally, that data is available in the state file using the normal Pillar
  443. lookup syntax. The following example is grabbing web server names and IP
  444. addresses from :ref:`Salt Mine <salt-mine>`. If this state is invoked from the
  445. Reactor then the custom Pillar value from above will be available and the new
  446. minion will be added to the pool but with the ``disabled`` flag so that HAProxy
  447. won't yet direct traffic to it.
  448. :file:`/srv/salt/haproxy/refresh_pool.sls`:
  449. .. code-block:: jinja
  450. {% set new_minion = salt['pillar.get']('new_minion') %}
  451. listen web *:80
  452. balance source
  453. {% for server,ip in salt['mine.get']('web*', 'network.interfaces', ['eth0']).items() %}
  454. {% if server == new_minion %}
  455. server {{ server }} {{ ip }}:80 disabled
  456. {% else %}
  457. server {{ server }} {{ ip }}:80 check
  458. {% endif %}
  459. {% endfor %}
  460. A Complete Example
  461. ==================
  462. In this example, we're going to assume that we have a group of servers that
  463. will come online at random and need to have keys automatically accepted. We'll
  464. also add that we don't want all servers being automatically accepted. For this
  465. example, we'll assume that all hosts that have an id that starts with 'ink'
  466. will be automatically accepted and have :py:func:`state.apply
  467. <salt.modules.state.apply_>` executed. On top of this, we're going to add that
  468. a host coming up that was replaced (meaning a new key) will also be accepted.
  469. Our master configuration will be rather simple. All minions that attempte to
  470. authenticate will match the :strong:`tag` of :strong:`salt/auth`. When it comes
  471. to the minion key being accepted, we get a more refined :strong:`tag` that
  472. includes the minion id, which we can use for matching.
  473. :file:`/etc/salt/master.d/reactor.conf`:
  474. .. code-block:: yaml
  475. reactor:
  476. - 'salt/auth':
  477. - /srv/reactor/auth-pending.sls
  478. - 'salt/minion/ink*/start':
  479. - /srv/reactor/auth-complete.sls
  480. In this SLS file, we say that if the key was rejected we will delete the key on
  481. the master and then also tell the master to ssh in to the minion and tell it to
  482. restart the minion, since a minion process will die if the key is rejected.
  483. We also say that if the key is pending and the id starts with ink we will
  484. accept the key. A minion that is waiting on a pending key will retry
  485. authentication every ten seconds by default.
  486. :file:`/srv/reactor/auth-pending.sls`:
  487. .. code-block:: jinja
  488. {# Ink server failed to authenticate -- remove accepted key #}
  489. {% if not data['result'] and data['id'].startswith('ink') %}
  490. minion_remove:
  491. wheel.key.delete:
  492. - args:
  493. - match: {{ data['id'] }}
  494. minion_rejoin:
  495. local.cmd.run:
  496. - tgt: salt-master.domain.tld
  497. - args:
  498. - cmd: ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "{{ data['id'] }}" 'sleep 10 && /etc/init.d/salt-minion restart'
  499. {% endif %}
  500. {# Ink server is sending new key -- accept this key #}
  501. {% if 'act' in data and data['act'] == 'pend' and data['id'].startswith('ink') %}
  502. minion_add:
  503. wheel.key.accept:
  504. - args:
  505. - match: {{ data['id'] }}
  506. {% endif %}
  507. No if statements are needed here because we already limited this action to just
  508. Ink servers in the master configuration.
  509. :file:`/srv/reactor/auth-complete.sls`:
  510. .. code-block:: jinja
  511. {# When an Ink server connects, run state.apply. #}
  512. highstate_run:
  513. local.state.apply:
  514. - tgt: {{ data['id'] }}
  515. - ret: smtp
  516. The above will also return the :ref:`highstate <running-highstate>` result data
  517. using the `smtp_return` returner (use virtualname like when using from the
  518. command line with `--return`). The returner needs to be configured on the
  519. minion for this to work. See :mod:`salt.returners.smtp_return
  520. <salt.returners.smtp_return>` documentation for that.
  521. .. _minion-start-reactor:
  522. Syncing Custom Types on Minion Start
  523. ====================================
  524. Salt will sync all custom types (by running a :mod:`saltutil.sync_all
  525. <salt.modules.saltutil.sync_all>`) on every :ref:`highstate
  526. <running-highstate>`. However, there is a chicken-and-egg issue where, on the
  527. initial :ref:`highstate <running-highstate>`, a minion will not yet have these
  528. custom types synced when the top file is first compiled. This can be worked
  529. around with a simple reactor which watches for ``minion_start`` events, which
  530. each minion fires when it first starts up and connects to the master.
  531. On the master, create **/srv/reactor/sync_grains.sls** with the following
  532. contents:
  533. .. code-block:: jinja
  534. sync_grains:
  535. local.saltutil.sync_grains:
  536. - tgt: {{ data['id'] }}
  537. And in the master config file, add the following reactor configuration:
  538. .. code-block:: yaml
  539. reactor:
  540. - 'salt/minion/*/start':
  541. - /srv/reactor/sync_grains.sls
  542. This will cause the master to instruct each minion to sync its custom grains
  543. when it starts, making these grains available when the initial :ref:`highstate
  544. <running-highstate>` is executed.
  545. Other types can be synced by replacing ``local.saltutil.sync_grains`` with
  546. ``local.saltutil.sync_modules``, ``local.saltutil.sync_all``, or whatever else
  547. suits the intended use case.
  548. Also, if it is not desirable that *every* minion syncs on startup, the ``*``
  549. can be replaced with a different glob to narrow down the set of minions which
  550. will match that reactor (e.g. ``salt/minion/appsrv*/start``, which would only
  551. match minion IDs beginning with ``appsrv``).