index.rst 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939
  1. .. _proxy-minion:
  2. =================
  3. Salt Proxy Minion
  4. =================
  5. Proxy minions are a developing Salt feature that enables controlling devices
  6. that, for whatever reason, cannot run a standard salt-minion. Examples include
  7. network gear that has an API but runs a proprietary OS, devices with limited
  8. CPU or memory, or devices that could run a minion, but for security reasons,
  9. will not.
  10. There are some :ref:`proxy modules <all-salt.proxy>` available, but if your device
  11. interface is not currently supported you will most likely have to write the interface
  12. yourself, because there are an infinite number of controllable devices. Fortunately, this
  13. is only as difficult as the actual interface to the proxied device. Devices that have an
  14. existing Python module (PyUSB for example) would be relatively simple to interface.
  15. Code to control a device that has an HTML REST-based interface should be easy. Code to
  16. control your typical housecat would be excellent source material for a PhD thesis.
  17. Salt proxy-minions provide the 'plumbing' that allows device enumeration
  18. and discovery, control, status, remote execution, and state management.
  19. See the :ref:`Proxy Minion Walkthrough <proxy-minion-end-to-end-example>` for an end-to-end
  20. demonstration of a working REST-based proxy minion.
  21. See the :ref:`Proxy Minion SSH Walkthrough <proxy-minion-ssh-end-to-end-example>` for an end-to-end
  22. demonstration of a working SSH proxy minion.
  23. See :ref:`Proxyminion States <proxy-minion-states>` to configure and
  24. run ``salt-proxy`` on a remote minion. Specify all your master side
  25. proxy (pillar) configuration and use this state to remotely configure proxies on one
  26. or more minions.
  27. See :ref:`Proxyminion Beacon <proxy-minion-beacon>` to help
  28. with easy configuration and management of ``salt-proxy`` processes.
  29. New in 2017.7.0
  30. ---------------
  31. The :conf_proxy:`proxy_merge_grains_in_module` configuration variable
  32. introduced in 2016.3, has been changed, defaulting to ``True``.
  33. The connection with the remote device is kept alive by default, when the
  34. module implements the ``alive`` function and :conf_proxy:`proxy_keep_alive`
  35. is set to ``True``. The polling interval is set using the
  36. :conf_proxy:`proxy_keep_alive_interval` option which defaults to 1 minute.
  37. The developers are also able to use the :conf_proxy:`proxy_always_alive`,
  38. when designing a proxy module flexible enough to open the
  39. connection with the remote device only when required.
  40. New in 2016.11.0
  41. ----------------
  42. Proxy minions now support configuration files with names ending in '\*.conf'
  43. and placed in /etc/salt/proxy.d.
  44. Proxy minions can now be configured in /etc/salt/proxy or /etc/salt/proxy.d
  45. instead of just pillar. Configuration format is the same as it would be in pillar.
  46. New in 2016.3
  47. -------------
  48. The deprecated config option ``enumerate_proxy_minions`` has been removed.
  49. As mentioned in earlier documentation, the ``add_proxymodule_to_opts``
  50. configuration variable defaults to ``False`` in this release. This means if you
  51. have proxymodules or other code looking in ``__opts__['proxymodule']`` you
  52. will need to set this variable in your ``/etc/salt/proxy`` file, or
  53. modify your code to use the `__proxy__` injected variable.
  54. The ``__proxyenabled__`` directive now only applies to grains and proxy modules
  55. themselves. Standard execution modules and state modules are not prevented
  56. from loading for proxy minions.
  57. Enhancements in grains processing have made the ``__proxyenabled__`` directive
  58. somewhat redundant in dynamic grains code. It is still required, but best
  59. practices for the ``__virtual__`` function in grains files have changed. It
  60. is now recommended that the ``__virtual__`` functions check to make sure
  61. they are being loaded for the correct proxytype, example below:
  62. .. code-block:: python
  63. def __virtual__():
  64. """
  65. Only work on proxy
  66. """
  67. try:
  68. if (
  69. salt.utils.platform.is_proxy()
  70. and __opts__["proxy"]["proxytype"] == "ssh_sample"
  71. ):
  72. return __virtualname__
  73. except KeyError:
  74. pass
  75. return False
  76. The try/except block above exists because grains are processed very early
  77. in the proxy minion startup process, sometimes earlier than the proxy
  78. key in the ``__opts__`` dictionary is populated.
  79. Grains are loaded so early in startup that no dunder dictionaries are
  80. present, so ``__proxy__``, ``__salt__``, etc. are not available. Custom
  81. grains located in ``/srv/salt/_grains`` and in the salt install grains
  82. directory can now take a single argument, ``proxy``, that is identical
  83. to ``__proxy__``. This enables patterns like
  84. .. code-block:: python
  85. def get_ip(proxy):
  86. """
  87. Ask the remote device what IP it has
  88. """
  89. return {"ip": proxy["proxymodulename.get_ip"]()}
  90. Then the grain ``ip`` will contain the result of calling the ``get_ip()`` function
  91. in the proxymodule called ``proxymodulename``.
  92. Proxy modules now benefit from including a function called ``initialized()``. This
  93. function should return ``True`` if the proxy's ``init()`` function has been successfully
  94. called. This is needed to make grains processing easier.
  95. Finally, if there is a function called ``grains`` in the proxymodule, it
  96. will be executed on proxy-minion startup and its contents will be merged with
  97. the rest of the proxy's grains. Since older proxy-minions might have used other
  98. methods to call such a function and add its results to grains, this is config-gated
  99. by a new proxy configuration option called ``proxy_merge_grains_in_module``. This
  100. defaults to ``True`` in the **2017.7.0** release.
  101. New in 2015.8.2
  102. ---------------
  103. *BREAKING CHANGE*: Adding the `proxymodule` variable to __opts__ is deprecated.
  104. The `proxymodule` variable has been moved a new globally-injected variable
  105. called `__proxy__`. A related configuration option called
  106. `add_proxymodule_to_opts` has been added and defaults to `True`. In the next
  107. major release, 2016.3.0, this variable will default to False.
  108. In the meantime, proxies that functioned under 2015.8.0 and .1 should continue
  109. to work under 2015.8.2. You should rework your proxy code to use `__proxy__` as
  110. soon as possible.
  111. The `rest_sample` example proxy minion has been updated to use `__proxy__`.
  112. This change was made because proxymodules are a LazyLoader object, but
  113. LazyLoaders cannot be serialized. `__opts__` gets serialized, and so things
  114. like `saltutil.sync_all` and `state.highstate` would throw exceptions.
  115. Support has been added to Salt's loader allowing custom proxymodules
  116. to be placed in ``salt://_proxy``. Proxy minions that need these modules
  117. will need to be restarted to pick up any changes. A corresponding utility function,
  118. ``saltutil.sync_proxymodules``, has been added to sync these modules to minions.
  119. In addition, a salt.utils helper function called `is_proxy()` was added to make
  120. it easier to tell when the running minion is a proxy minion. **NOTE: This
  121. function was renamed to salt.utils.platform.is_proxy() for the 2018.3.0
  122. release**
  123. New in 2015.8
  124. -------------
  125. Starting with the 2015.8 release of Salt, proxy processes are no longer forked
  126. off from a controlling minion. Instead, they have their own script
  127. ``salt-proxy`` which takes mostly the same arguments that the standard Salt
  128. minion does with the addition of ``--proxyid``. This is the id that the
  129. salt-proxy will use to identify itself to the master. Proxy configurations are
  130. still best kept in Pillar and their format has not changed.
  131. This change allows for better process control and logging. Proxy processes can
  132. now be listed with standard process management utilities (``ps`` from the
  133. command line). Also, a full Salt minion is no longer required (though it is
  134. still strongly recommended) on machines hosting proxies.
  135. Getting Started
  136. ---------------
  137. The following diagram may be helpful in understanding the structure of a Salt
  138. installation that includes proxy-minions:
  139. .. image:: /_static/proxy_minions.png
  140. The key thing to remember is the left-most section of the diagram. Salt's
  141. nature is to have a minion connect to a master, then the master may control
  142. the minion. However, for proxy minions, the target device cannot run a minion.
  143. After the proxy minion is started and initiates its connection to the
  144. device, it connects back to the salt-master and for all intents and purposes
  145. looks like just another minion to the Salt master.
  146. To create support for a proxied device one needs to create four things:
  147. 1. The `proxy_connection_module`_ (located in salt/proxy).
  148. 2. The `grains support code`_ (located in salt/grains).
  149. 3. :ref:`Salt modules <all-salt.modules>` specific to the controlled
  150. device.
  151. 4. :ref:`Salt states <all-salt.states>` specific to the controlled device.
  152. Configuration parameters
  153. ########################
  154. Proxy minions require no configuration parameters in /etc/salt/master.
  155. Salt's Pillar system is ideally suited for configuring proxy-minions
  156. (though they can be configured in /etc/salt/proxy as well). Proxies
  157. can either be designated via a pillar file in pillar_roots, or through an
  158. external pillar. External pillars afford the opportunity for interfacing with
  159. a configuration management system, database, or other knowledgeable system that
  160. that may already contain all the details of proxy targets. To use static files
  161. in pillar_roots, pattern your files after the following examples, which are
  162. based on the diagram above:
  163. ``/srv/pillar/top.sls``
  164. .. code-block:: yaml
  165. base:
  166. net-device1:
  167. - net-device1
  168. net-device2:
  169. - net-device2
  170. net-device3:
  171. - net-device3
  172. i2c-device4:
  173. - i2c-device4
  174. i2c-device5:
  175. - i2c-device5
  176. 433wireless-device6:
  177. - 433wireless-device6
  178. smsgate-device7:
  179. - device7
  180. ``/srv/pillar/net-device1.sls``
  181. .. code-block:: yaml
  182. proxy:
  183. proxytype: networkswitch
  184. host: 172.23.23.5
  185. username: root
  186. passwd: letmein
  187. ``/srv/pillar/net-device2.sls``
  188. .. code-block:: yaml
  189. proxy:
  190. proxytype: networkswitch
  191. host: 172.23.23.6
  192. username: root
  193. passwd: letmein
  194. ``/srv/pillar/net-device3.sls``
  195. .. code-block:: yaml
  196. proxy:
  197. proxytype: networkswitch
  198. host: 172.23.23.7
  199. username: root
  200. passwd: letmein
  201. ``/srv/pillar/i2c-device4.sls``
  202. .. code-block:: yaml
  203. proxy:
  204. proxytype: i2c_lightshow
  205. i2c_address: 1
  206. ``/srv/pillar/i2c-device5.sls``
  207. .. code-block:: yaml
  208. proxy:
  209. proxytype: i2c_lightshow
  210. i2c_address: 2
  211. ``/srv/pillar/433wireless-device6.sls``
  212. .. code-block:: yaml
  213. proxy:
  214. proxytype: 433mhz_wireless
  215. ``/srv/pillar/smsgate-device7.sls``
  216. .. code-block:: yaml
  217. proxy:
  218. proxytype: sms_serial
  219. deventry: /dev/tty04
  220. Note the contents of each minioncontroller key may differ widely based on
  221. the type of device that the proxy-minion is managing.
  222. In the above example
  223. - net-devices 1, 2, and 3 are network switches that have a management
  224. interface available at a particular IP address.
  225. - i2c-devices 4 and 5 are very low-level devices controlled over an i2c bus.
  226. In this case the devices are physically connected to machine
  227. 'minioncontroller2', and are addressable on the i2c bus at their respective
  228. i2c addresses.
  229. - 433wireless-device6 is a 433 MHz wireless transmitter, also physically connected to
  230. minioncontroller2
  231. - smsgate-device7 is an SMS gateway connected to machine minioncontroller3 via a
  232. serial port.
  233. Because of the way pillar works, each of the salt-proxy processes that fork off the
  234. proxy minions will only see the keys specific to the proxies it will be
  235. handling.
  236. Proxies can be configured in /etc/salt/proxy or with files in /etc/salt/proxy.d as of
  237. Salt's 2016.11.0 release.
  238. Also, in general, proxy-minions are lightweight, so the machines that run them
  239. could conceivably control a large number of devices. To run more than one proxy from
  240. a single machine, simply start an additional proxy process with ``--proxyid``
  241. set to the id to which you want the proxy to bind.
  242. It is possible for the proxy services to be spread across
  243. many machines if necessary, or intentionally run on machines that need to
  244. control devices because of some physical interface (e.g. i2c and serial above).
  245. Another reason to divide proxy services might be security. In more secure
  246. environments only certain machines may have a network path to certain devices.
  247. .. _proxy_connection_module:
  248. Proxymodules
  249. ############
  250. A proxy module encapsulates all the code necessary to interface with a device.
  251. Proxymodules are located inside the salt.proxy module, or can be placed in
  252. the ``_proxy`` directory in your file_roots (default is ``/srv/salt/_proxy``.
  253. At a minimum a proxymodule object must implement the following functions:
  254. ``__virtual__()``: This function performs the same duty that it does for other
  255. types of Salt modules. Logic goes here to determine if the module can be
  256. loaded, checking for the presence of Python modules on which the proxy depends.
  257. Returning ``False`` will prevent the module from loading.
  258. ``init(opts)``: Perform any initialization that the device needs. This is
  259. a good place to bring up a persistent connection to a device, or authenticate
  260. to create a persistent authorization token.
  261. ``initialized()``: Returns True if ``init()`` was successfully called.
  262. ``shutdown()``: Code to cleanly shut down or close a connection to
  263. a controlled device goes here. This function must exist, but can contain only
  264. the keyword ``pass`` if there is no shutdown logic required.
  265. ``ping()``: While not required, it is highly recommended that this function also
  266. be defined in the proxymodule. The code for ``ping`` should contact the
  267. controlled device and make sure it is really available.
  268. ``alive(opts)``: Another optional function, it is used together with the
  269. ``proxy_keep_alive`` option (default: ``True``). This function should
  270. return a boolean value corresponding to the state of the connection.
  271. If the connection is down, will try to restart (``shutdown``
  272. followed by ``init``). The polling frequency is controlled using
  273. the ``proxy_keep_alive_interval`` option, in minutes.
  274. ``grains()``: Rather than including grains in /srv/salt/_grains or in
  275. the standard install directories for grains, grains can be computed and
  276. returned by this function. This function will be called automatically
  277. if ``proxy_merge_grains_in_module`` is set to ``True`` in /etc/salt/proxy.
  278. This variable defaults to ``True`` in the release code-named *2017.7.0*.
  279. Pre 2015.8 the proxymodule also must have an ``id()`` function. 2015.8 and following don't use
  280. this function because the proxy's id is required on the command line.
  281. Here is an example proxymodule used to interface to a *very* simple REST
  282. server. Code for the server is in the `salt-contrib GitHub repository`_.
  283. .. _`salt-contrib GitHub repository`: https://github.com/saltstack/salt-contrib/tree/master/proxyminion_rest_example
  284. This proxymodule enables "service" enumeration, starting, stopping, restarting,
  285. and status; "package" installation, and a ping.
  286. .. code-block:: python
  287. # -*- coding: utf-8 -*-
  288. """
  289. This is a simple proxy-minion designed to connect to and communicate with
  290. the bottle-based web service contained in https://github.com/saltstack/salt-contrib/tree/master/proxyminion_rest_example
  291. """
  292. from __future__ import absolute_import
  293. # Import python libs
  294. import logging
  295. import salt.utils.http
  296. HAS_REST_EXAMPLE = True
  297. # This must be present or the Salt loader won't load this module
  298. __proxyenabled__ = ["rest_sample"]
  299. # Variables are scoped to this module so we can have persistent data
  300. # across calls to fns in here.
  301. GRAINS_CACHE = {}
  302. DETAILS = {}
  303. # Want logging!
  304. log = logging.getLogger(__file__)
  305. # This does nothing, it's here just as an example and to provide a log
  306. # entry when the module is loaded.
  307. def __virtual__():
  308. """
  309. Only return if all the modules are available
  310. """
  311. log.debug("rest_sample proxy __virtual__() called...")
  312. return True
  313. def _complicated_function_that_determines_if_alive():
  314. return True
  315. # Every proxy module needs an 'init', though you can
  316. # just put DETAILS['initialized'] = True here if nothing
  317. # else needs to be done.
  318. def init(opts):
  319. log.debug("rest_sample proxy init() called...")
  320. DETAILS["initialized"] = True
  321. # Save the REST URL
  322. DETAILS["url"] = opts["proxy"]["url"]
  323. # Make sure the REST URL ends with a '/'
  324. if not DETAILS["url"].endswith("/"):
  325. DETAILS["url"] += "/"
  326. def alive(opts):
  327. """
  328. This function returns a flag with the connection state.
  329. It is very useful when the proxy minion establishes the communication
  330. via a channel that requires a more elaborated keep-alive mechanism, e.g.
  331. NETCONF over SSH.
  332. """
  333. log.debug("rest_sample proxy alive() called...")
  334. return _complicated_function_that_determines_if_alive()
  335. def initialized():
  336. """
  337. Since grains are loaded in many different places and some of those
  338. places occur before the proxy can be initialized, return whether
  339. our init() function has been called
  340. """
  341. return DETAILS.get("initialized", False)
  342. def grains():
  343. """
  344. Get the grains from the proxied device
  345. """
  346. if not DETAILS.get("grains_cache", {}):
  347. r = salt.utils.http.query(
  348. DETAILS["url"] + "info", decode_type="json", decode=True
  349. )
  350. DETAILS["grains_cache"] = r["dict"]
  351. return DETAILS["grains_cache"]
  352. def grains_refresh():
  353. """
  354. Refresh the grains from the proxied device
  355. """
  356. DETAILS["grains_cache"] = None
  357. return grains()
  358. def fns():
  359. return {
  360. "details": "This key is here because a function in "
  361. "grains/rest_sample.py called fns() here in the proxymodule."
  362. }
  363. def service_start(name):
  364. """
  365. Start a "service" on the REST server
  366. """
  367. r = salt.utils.http.query(
  368. DETAILS["url"] + "service/start/" + name, decode_type="json", decode=True
  369. )
  370. return r["dict"]
  371. def service_stop(name):
  372. """
  373. Stop a "service" on the REST server
  374. """
  375. r = salt.utils.http.query(
  376. DETAILS["url"] + "service/stop/" + name, decode_type="json", decode=True
  377. )
  378. return r["dict"]
  379. def service_restart(name):
  380. """
  381. Restart a "service" on the REST server
  382. """
  383. r = salt.utils.http.query(
  384. DETAILS["url"] + "service/restart/" + name, decode_type="json", decode=True
  385. )
  386. return r["dict"]
  387. def service_list():
  388. """
  389. List "services" on the REST server
  390. """
  391. r = salt.utils.http.query(
  392. DETAILS["url"] + "service/list", decode_type="json", decode=True
  393. )
  394. return r["dict"]
  395. def service_status(name):
  396. """
  397. Check if a service is running on the REST server
  398. """
  399. r = salt.utils.http.query(
  400. DETAILS["url"] + "service/status/" + name, decode_type="json", decode=True
  401. )
  402. return r["dict"]
  403. def package_list():
  404. """
  405. List "packages" installed on the REST server
  406. """
  407. r = salt.utils.http.query(
  408. DETAILS["url"] + "package/list", decode_type="json", decode=True
  409. )
  410. return r["dict"]
  411. def package_install(name, **kwargs):
  412. """
  413. Install a "package" on the REST server
  414. """
  415. cmd = DETAILS["url"] + "package/install/" + name
  416. if kwargs.get("version", False):
  417. cmd += "/" + kwargs["version"]
  418. else:
  419. cmd += "/1.0"
  420. r = salt.utils.http.query(cmd, decode_type="json", decode=True)
  421. return r["dict"]
  422. def fix_outage():
  423. r = salt.utils.http.query(DETAILS["url"] + "fix_outage")
  424. return r
  425. def uptodate(name):
  426. """
  427. Call the REST endpoint to see if the packages on the "server" are up to date.
  428. """
  429. r = salt.utils.http.query(
  430. DETAILS["url"] + "package/remove/" + name, decode_type="json", decode=True
  431. )
  432. return r["dict"]
  433. def package_remove(name):
  434. """
  435. Remove a "package" on the REST server
  436. """
  437. r = salt.utils.http.query(
  438. DETAILS["url"] + "package/remove/" + name, decode_type="json", decode=True
  439. )
  440. return r["dict"]
  441. def package_status(name):
  442. """
  443. Check the installation status of a package on the REST server
  444. """
  445. r = salt.utils.http.query(
  446. DETAILS["url"] + "package/status/" + name, decode_type="json", decode=True
  447. )
  448. return r["dict"]
  449. def ping():
  450. """
  451. Is the REST server up?
  452. """
  453. r = salt.utils.http.query(DETAILS["url"] + "ping", decode_type="json", decode=True)
  454. try:
  455. return r["dict"].get("ret", False)
  456. except Exception:
  457. return False
  458. def shutdown(opts):
  459. """
  460. For this proxy shutdown is a no-op
  461. """
  462. log.debug("rest_sample proxy shutdown() called...")
  463. .. _grains support code:
  464. Grains are data about minions. Most proxied devices will have a paltry amount
  465. of data as compared to a typical Linux server. By default, a proxy minion will
  466. have several grains taken from the host. Salt core code requires values for ``kernel``,
  467. ``os``, and ``os_family``--all of these are forced to be ``proxy`` for proxy-minions.
  468. To add others to your proxy minion for
  469. a particular device, create a file in salt/grains named [proxytype].py and place
  470. inside it the different functions that need to be run to collect the data you
  471. are interested in. Here's an example. Note the function below called ``proxy_functions``.
  472. It demonstrates how a grains function can take a single argument, which will be
  473. set to the value of ``__proxy__``. Dunder variables are not yet injected into Salt processes
  474. at the time grains are loaded, so this enables us to get a handle to the proxymodule so we
  475. can cross-call the functions therein used to communicate with the controlled device.
  476. Note that as of 2016.3, grains values can also be calculated in a function called ``grains()``
  477. in the proxymodule itself. This might be useful if a proxymodule author wants to keep
  478. all the code for the proxy interface in the same place instead of splitting it between
  479. the proxy and grains directories.
  480. This function will only be called automatically if the configuration variable
  481. ``proxy_merge_grains_in_module`` is set to True in the proxy configuration file
  482. (default ``/etc/salt/proxy``). This variable defaults to ``True`` in the
  483. release code-named *2017.7.0*.
  484. .. code: python::
  485. # -*- coding: utf-8 -*-
  486. '''
  487. Generate baseline proxy minion grains
  488. '''
  489. from __future__ import absolute_import
  490. import salt.utils.platform
  491. __proxyenabled__ = ['rest_sample']
  492. __virtualname__ = 'rest_sample'
  493. def __virtual__():
  494. try:
  495. if salt.utils.platform.is_proxy() and __opts__['proxy']['proxytype'] == 'rest_sample':
  496. return __virtualname__
  497. except KeyError:
  498. pass
  499. return False
  500. def kernel():
  501. return {'kernel': 'proxy'}
  502. def proxy_functions(proxy):
  503. '''
  504. The loader will execute functions with one argument and pass
  505. a reference to the proxymodules LazyLoader object. However,
  506. grains sometimes get called before the LazyLoader object is setup
  507. so `proxy` might be None.
  508. '''
  509. if proxy:
  510. return {'proxy_functions': proxy['rest_sample.fns']()}
  511. def os():
  512. return {'os': 'RestExampleOS'}
  513. def location():
  514. return {'location': 'In this darn virtual machine. Let me out!'}
  515. def os_family():
  516. return {'os_family': 'proxy'}
  517. def os_data():
  518. return {'os_data': 'funkyHttp release 1.0.a.4.g'}
  519. The __proxyenabled__ directive
  520. ------------------------------
  521. In previous versions of Salt the ``__proxyenabled__`` directive controlled
  522. loading of all Salt modules for proxies (e.g. grains, execution modules, state
  523. modules). From 2016.3 on, the only modules that respect ``__proxyenabled__``
  524. are grains and proxy modules. These modules need to be told which proxy they
  525. work with.
  526. ``__proxyenabled__`` is a list, and can contain a single '*' to indicate
  527. a grains module works with all proxies.
  528. Example from ``salt/grains/rest_sample.py``:
  529. .. code-block:: python
  530. # -*- coding: utf-8 -*-
  531. """
  532. Generate baseline proxy minion grains
  533. """
  534. from __future__ import absolute_import
  535. import salt.utils.platform
  536. __proxyenabled__ = ["rest_sample"]
  537. __virtualname__ = "rest_sample"
  538. def __virtual__():
  539. try:
  540. if (
  541. salt.utils.platform.is_proxy()
  542. and __opts__["proxy"]["proxytype"] == "rest_sample"
  543. ):
  544. return __virtualname__
  545. except KeyError:
  546. pass
  547. return False
  548. .. toctree::
  549. :maxdepth: 2
  550. :glob:
  551. demo
  552. SSH Proxymodules
  553. ----------------
  554. See above for a general introduction to writing proxy modules.
  555. All of the guidelines that apply to REST are the same for SSH.
  556. This sections specifically talks about the SSH proxy module and
  557. explains the working of the example proxy module ``ssh_sample``.
  558. Here is a simple example proxymodule used to interface to a device over SSH.
  559. Code for the SSH shell is in the `salt-contrib GitHub repository`_.
  560. This proxymodule enables "package" installation.
  561. .. code-block:: python
  562. # -*- coding: utf-8 -*-
  563. """
  564. This is a simple proxy-minion designed to connect to and communicate with
  565. a server that exposes functionality via SSH.
  566. This can be used as an option when the device does not provide
  567. an api over HTTP and doesn't have the python stack to run a minion.
  568. """
  569. from __future__ import absolute_import
  570. # Import python libs
  571. import salt.utils.json
  572. import logging
  573. # Import Salt's libs
  574. from salt.utils.vt_helper import SSHConnection
  575. from salt.utils.vt import TerminalException
  576. # This must be present or the Salt loader won't load this module
  577. __proxyenabled__ = ["ssh_sample"]
  578. DETAILS = {}
  579. # Want logging!
  580. log = logging.getLogger(__file__)
  581. # This does nothing, it's here just as an example and to provide a log
  582. # entry when the module is loaded.
  583. def __virtual__():
  584. """
  585. Only return if all the modules are available
  586. """
  587. log.info("ssh_sample proxy __virtual__() called...")
  588. return True
  589. def init(opts):
  590. """
  591. Required.
  592. Can be used to initialize the server connection.
  593. """
  594. try:
  595. DETAILS["server"] = SSHConnection(
  596. host=__opts__["proxy"]["host"],
  597. username=__opts__["proxy"]["username"],
  598. password=__opts__["proxy"]["password"],
  599. )
  600. # connected to the SSH server
  601. out, err = DETAILS["server"].sendline("help")
  602. except TerminalException as e:
  603. log.error(e)
  604. return False
  605. def shutdown(opts):
  606. """
  607. Disconnect
  608. """
  609. DETAILS["server"].close_connection()
  610. def parse(out):
  611. """
  612. Extract json from out.
  613. Parameter
  614. out: Type string. The data returned by the
  615. ssh command.
  616. """
  617. jsonret = []
  618. in_json = False
  619. for ln_ in out.split("\n"):
  620. if "{" in ln_:
  621. in_json = True
  622. if in_json:
  623. jsonret.append(ln_)
  624. if "}" in ln_:
  625. in_json = False
  626. return salt.utils.json.loads("\n".join(jsonret))
  627. def package_list():
  628. """
  629. List "packages" by executing a command via ssh
  630. This function is called in response to the salt command
  631. ..code-block::bash
  632. salt target_minion pkg.list_pkgs
  633. """
  634. # Send the command to execute
  635. out, err = DETAILS["server"].sendline("pkg_list")
  636. # "scrape" the output and return the right fields as a dict
  637. return parse(out)
  638. def package_install(name, **kwargs):
  639. """
  640. Install a "package" on the REST server
  641. """
  642. cmd = "pkg_install " + name
  643. if "version" in kwargs:
  644. cmd += "/" + kwargs["version"]
  645. else:
  646. cmd += "/1.0"
  647. # Send the command to execute
  648. out, err = DETAILS["server"].sendline(cmd)
  649. # "scrape" the output and return the right fields as a dict
  650. return parse(out)
  651. def package_remove(name):
  652. """
  653. Remove a "package" on the REST server
  654. """
  655. cmd = "pkg_remove " + name
  656. # Send the command to execute
  657. out, err = DETAILS["server"].sendline(cmd)
  658. # "scrape" the output and return the right fields as a dict
  659. return parse(out)
  660. Connection Setup
  661. ################
  662. The ``init()`` method is responsible for connection setup. It uses the ``host``, ``username`` and ``password`` config variables defined in the pillar data. The ``prompt`` kwarg can be passed to ``SSHConnection`` if your SSH server's prompt differs from the example's prompt ``(Cmd)``. Instantiating the ``SSHConnection`` class establishes an SSH connection to the ssh server (using Salt VT).
  663. Command execution
  664. #################
  665. The ``package_*`` methods use the SSH connection (established in ``init()``) to send commands out to the SSH server. The ``sendline()`` method of ``SSHConnection`` class can be used to send commands out to the server. In the above example we send commands like ``pkg_list`` or ``pkg_install``. You can send any SSH command via this utility.
  666. Output parsing
  667. ##############
  668. Output returned by ``sendline()`` is a tuple of strings representing the stdout and the stderr respectively. In the toy example shown we simply scrape the output and convert it to a python dictionary, as shown in the ``parse`` method. You can tailor this method to match your parsing logic.
  669. Connection teardown
  670. ###################
  671. The ``shutdown`` method is responsible for calling the ``close_connection()`` method of ``SSHConnection`` class. This ends the SSH connection to the server.
  672. For more information please refer to class `SSHConnection`_.
  673. .. toctree::
  674. :maxdepth: 2
  675. :glob:
  676. ssh
  677. beacon
  678. state
  679. ../tutorials/esxi_proxy_minion
  680. .. _SSHConnection: https://github.com/saltstack/salt/blob/b8271c7512da7e048019ee26422be9e7d6b795ab/salt/utils/vt_helper.py#L28