neulab/tldr huggingface dataset:
Note, external WSGI servers handle URLs, paths, and SSL certs directly. The rest_cherrypy configuration options are ignored and the salt-api daemon does not need to be running at all. Remember Salt authentication credentials are sent in the clear unless SSL is being enforced!
An example Apache virtual host configuration:
<VirtualHost *:80> ServerName example.com ServerAlias *.example.com
ServerAdmin webmaster@example.com
LogLevel warn ErrorLog /var/www/example.com/logs/error.log CustomLog /var/www/example.com/logs/access.log combined
DocumentRoot /var/www/example.com/htdocs
WSGIScriptAlias / /path/to/salt/netapi/rest_cherrypy/wsgi.py </VirtualHost>
REST URI Reference / class salt.netapi.rest_cherrypy.app.LowDataAdapter The primary entry point to Salt's REST API
GET()  An explanation of the API with links of where to go next
• Accept -- the desired response format.
• 200 -- success
• 401 -- authentication required
• 406 -- requested Content-Type not available
Example request:
curl -i localhost:8000
GET / HTTP/1.1 Host: localhost:8000 Accept: application/json
Example response:
HTTP/1.1 200 OK Content-Type: application/json
POST(**kwargs) Send one or more Salt commands in the request body
POST /
• X-Auth-Token -- a session token from Login.
• Content-Type -- the format of the request body.
• Content-Type -- the format of the response body; depends on the Accept request header.
• 400 -- bad or malformed request
lowstate data describing Salt commands must be sent in the request body.
curl -sSik https://localhost:8000 \ -b ~/cookies.txt \ -H "Accept: application/x-yaml" \ -H "Content-type: application/json" \ -d '[{"client": "local", "tgt": "*", "fun": "test.ping"}]'
POST / HTTP/1.1 Host: localhost:8000 Accept: application/x-yaml X-Auth-Token: d40d1e1e Content-Type: application/json
[{"client": "local", "tgt": "*", "fun": "test.ping"}]
HTTP/1.1 200 OK Content-Length: 200 Allow: GET, HEAD, POST Content-Type: application/x-yaml
return: - ms-0: true ms-1: true ms-2: true ms-3: true ms-4: true
/login class salt.netapi.rest_cherrypy.app.Login(*args, **kwargs) Log in to receive a session token
Authentication information.
GET()  Present the login interface
GET /login An explanation of how to log in.
curl -i localhost:8000/login
GET /login HTTP/1.1 Host: localhost:8000 Accept: text/html
HTTP/1.1 200 OK Content-Type: text/html
POST(**kwargs) Authenticate against Salt's eauth system
POST /login
Form Parameters
• eauth -- the eauth backend configured for the user
• username -- username
• password -- password
curl -si localhost:8000/login \ -c ~/cookies.txt \ -H "Accept: application/json" \ -H "Content-type: application/json" \ -d '{ "username": "saltuser", "password": "saltuser", "eauth": "auto" }'
POST / HTTP/1.1 Host: localhost:8000 Content-Length: 42 Content-Type: application/json Accept: application/json
{"username": "saltuser", "password": "saltuser", "eauth": "auto"}
HTTP/1.1 200 OK Content-Type: application/json Content-Length: 206 X-Auth-Token: 6d1b722e Set-Cookie: session_id=6d1b722e; expires=Sat, 17 Nov 2012 03:23:52 GMT; Path=/
{"return": { "token": "6d1b722e", "start": 1363805943.776223, "expire": 1363849143.776224, "user": "saltuser", "eauth": "pam", "perms": [ "grains.*", "status.*", "sys.*", "test.*" ] }}
/logout class salt.netapi.rest_cherrypy.app.Logout Class to remove or invalidate sessions
POST() Destroy the currently active session and expire the session cookie
/minions class salt.netapi.rest_cherrypy.app.Minions Convenience URLs for working with minions
GET(mid=None) A convenience URL for getting lists of minions or getting minion details
GET /minions/(mid)
curl -i localhost:8000/minions/ms-3
GET /minions/ms-3 HTTP/1.1 Host: localhost:8000 Accept: application/x-yaml
HTTP/1.1 200 OK Content-Length: 129005 Content-Type: application/x-yaml
return: - ms-3: grains.items: ...
POST(**kwargs) Start an execution command and immediately return the job id
POST /minions
Lowstate data describing Salt commands must be sent in the request body. The client option will be set to local_async().
curl -sSi localhost:8000/minions \ -b ~/cookies.txt \ -H "Accept: application/x-yaml" \ -d '[{"tgt": "*", "fun": "status.diskusage"}]'
POST /minions HTTP/1.1 Host: localhost:8000 Accept: application/x-yaml Content-Type: application/json
tgt=*&fun=status.diskusage
HTTP/1.1 202 Accepted Content-Length: 86 Content-Type: application/x-yaml
return: - jid: '20130603122505459265' minions: [ms-4, ms-3, ms-2, ms-1, ms-0] _links: jobs: - href: /jobs/20130603122505459265
/jobs class salt.netapi.rest_cherrypy.app.Jobs
GET(jid=None, timeout='') A convenience URL for getting lists of previously run jobs or getting the return from a single job
GET /jobs/(jid) List jobs or show a single job from the job cache.
curl -i localhost:8000/jobs
GET /jobs HTTP/1.1 Host: localhost:8000 Accept: application/x-yaml
HTTP/1.1 200 OK Content-Length: 165 Content-Type: application/x-yaml
return: - '20121130104633606931': Arguments: - '3' Function: test.fib Start Time: 2012, Nov 30 10:46:33.606931 Target: jerry Target-type: glob
curl -i localhost:8000/jobs/20121130104633606931
GET /jobs/20121130104633606931 HTTP/1.1 Host: localhost:8000 Accept: application/x-yaml
HTTP/1.1 200 OK Content-Length: 73 Content-Type: application/x-yaml
info: - Arguments: - '3' Function: test.fib Minions: - jerry Start Time: 2012, Nov 30 10:46:33.606931 Target: '*' Target-type: glob User: saltdev jid: '20121130104633606931' return: - jerry: - - 0 - 1 - 1 - 2 - 6.9141387939453125e-06
/run class salt.netapi.rest_cherrypy.app.Run Run commands bypassing the normal session handling.
salt-api does not enforce authorization, Salt's eauth system does that.  Local/Runner/WheelClient all accept username/password/eauth or token kwargs that are then checked by the eauth system. The session mechanism in rest_cherrypy simply pairs a session with a Salt eauth token and then passes the token kwarg in automatically.
If you already have a Salt eauth token, perhaps generated by the mk_token function in the Auth Runner module, then there is no reason to use sessions.
This endpoint accepts either a username, password, eauth trio, or a token kwarg and does not make use of sessions at all.
POST(**kwargs) Run commands bypassing the normal session handling. Otherwise, this URL is identical to the root URL (/).
POST /run An array of lowstate data describing Salt commands must be sent in the request body.
curl -sS localhost:8000/run \ -H 'Accept: application/x-yaml' \ -H 'Content-type: application/json' \ -d '[{ "client": "local", "tgt": "*", "fun": "test.ping", "username": "saltdev", "password": "saltdev", "eauth": "auto" }]'
Or using a Salt Eauth token:
curl -sS localhost:8000/run \ -H 'Accept: application/x-yaml' \ -H 'Content-type: application/json' \ -d '[{ "client": "local", "tgt": "*", "fun": "test.ping", "token": "<salt eauth token here>" }]'
POST /run HTTP/1.1 Host: localhost:8000 Accept: application/x-yaml Content-Length: 75 Content-Type: application/json
[{"client": "local", "tgt": "*", "fun": "test.ping", "username": "saltdev", "password": "saltdev", "eauth": "auto"}]
The /run endpoint can also be used to issue commands using the salt-ssh subsystem.  When using salt-ssh, eauth credentials must also be supplied, and are subject to eauth access-control lists.
All SSH client requests are synchronous.
Example SSH client request:
curl -sS localhost:8000/run \ -H 'Accept: application/x-yaml' \ -d client='ssh' \ -d tgt='*' \ -d username='saltdev' \ -d password='saltdev' \ -d eauth='auto' \ -d fun='test.ping'
POST /run HTTP/1.1 Host: localhost:8000 Accept: application/x-yaml Content-Length: 75 Content-Type: application/x-www-form-urlencoded
Example SSH response:
return: - silver: _stamp: '2020-09-08T23:04:28.912609' fun: test.ping fun_args: [] id: silver jid: '20200908230427905565' retcode: 0 return: true
/events class salt.netapi.rest_cherrypy.app.Events Expose the Salt event bus
The event bus on the Salt master exposes a large variety of things, notably when executions are started on the master and also when minions ultimately return their results. This URL provides a real-time window into a running Salt infrastructure.
SEE ALSO: events
GET(token=None, salt_token=None) An HTTP stream of the Salt master event bus
This stream is formatted per the Server Sent Events (SSE) spec. Each event is formatted as JSON.
GET /events
• token -- optional parameter containing the token ordinarily supplied via the X-Auth-Token header in order to allow cross-domain requests in browsers that do not include CORS support in the EventSource API. E.g., curl -NsS localhost:8000/events?token=308650d
• salt_token -- optional parameter containing a raw Salt eauth token (not to be confused with the token returned from the /login URL). E.g., curl -NsS localhost:8000/events?salt_token=30742765
curl -NsS localhost:8000/events
GET /events HTTP/1.1 Host: localhost:8000
Note, the tag field is not part of the spec. SSE compliant clients should ignore unknown fields. This addition allows non-compliant clients to only watch for certain tags without having to deserialze the JSON object each time.
HTTP/1.1 200 OK Connection: keep-alive Cache-Control: no-cache Content-Type: text/event-stream;charset=utf-8
retry: 400
tag: salt/job/20130802115730568475/new data: {'tag': 'salt/job/20130802115730568475/new', 'data': {'minions': ['ms-4', 'ms-3', 'ms-2', 'ms-1', 'ms-0']}}
tag: salt/job/20130802115730568475/ret/jerry data: {'tag': 'salt/job/20130802115730568475/ret/jerry', 'data': {'jid': '20130802115730568475', 'return': True, 'retcode': 0, 'success': True, 'cmd': '_return', 'fun': 'test.ping', 'id': 'ms-1'}}
The event stream can be easily consumed via JavaScript:
var source = new EventSource('/events'); source.onopen = function() { console.info('Listening ...') }; source.onerror = function(err) { console.error(err) }; source.onmessage = function(message) { var saltEvent = JSON.parse(message.data); console.log(saltEvent.tag, saltEvent.data); };
Note, the SSE stream is fast and completely asynchronous and Salt is very fast. If a job is created using a regular POST request, it is possible that the job return will be available on the SSE stream before the response for the POST request arrives. It is important to take that asynchronicity into account when designing an application. Below are some general guidelines.
• Subscribe to the SSE stream _before_ creating any events.
• Process SSE events directly as they arrive and don't wait for any other process to "complete" first (like an ajax request).
• Keep a buffer of events if the event stream must be used for synchronous lookups.
• Be cautious in writing Salt's event stream directly to the DOM. It is very busy and can quickly overwhelm the memory allocated to a browser tab.
A full, working proof-of-concept JavaScript application is available adjacent to this file.  It can be viewed by pointing a browser at the /app endpoint in a running rest_cherrypy instance.
Or using CORS:
var source = new EventSource('/events?token=ecd589e4e01912cf3c4035afad73426dbb8dba75', {withCredentials: true});
It is also possible to consume the stream via the shell.
Records are separated by blank lines; the data: and tag: prefixes will need to be removed manually before attempting to unserialize the JSON.
curl's -N flag turns off input buffering which is required to process the stream incrementally.
Here is a basic example of printing each event as it comes in:
curl -NsS localhost:8000/events |\ while IFS= read -r line ; do echo $line done
Here is an example of using awk to filter events based on tag:
curl -NsS localhost:8000/events |\ awk ' BEGIN { RS=""; FS="\\n" } $1 ~ /^tag: salt\/job\/[0-9]+\/new$/ { print $0 } ' tag: salt/job/20140112010149808995/new data: {"tag": "salt/job/20140112010149808995/new", "data": {"tgt_type": "glob", "jid": "20140112010149808995", "tgt": "jerry", "_stamp": "2014-01-12_01:01:49.809617", "user": "shouse", "arg": [], "fun": "test.ping", "minions": ["jerry"]}} tag: 20140112010149808995 data: {"tag": "20140112010149808995", "data": {"fun_args": [], "jid": "20140112010149808995", "return": true, "retcode": 0, "success": true, "cmd": "_return", "_stamp": "2014-01-12_01:01:49.819316", "fun": "test.ping", "id": "jerry"}}
/hook class salt.netapi.rest_cherrypy.app.Webhook A generic web hook entry point that fires an event on Salt's event bus
External services can POST data to this URL to trigger an event in Salt.  For example, Amazon SNS, Jenkins-CI or Travis-CI, or GitHub web hooks.
NOTE: Be mindful of security
Salt's Reactor can run any code. A Reactor SLS that responds to a hook event is responsible for validating that the event came from a trusted source and contains valid data.
This is a generic interface and securing it is up to you!
This URL requires authentication however not all external services can be configured to authenticate. For this reason authentication can be selectively disabled for this URL. Follow best practices -- always use SSL, pass a secret key, configure the firewall to only allow traffic from a known source, etc.
The event data is taken from the request body. The Content-Type header is respected for the payload.
The event tag is prefixed with salt/netapi/hook and the URL path is appended to the end. For example, a POST request sent to /hook/mycompany/myapp/mydata will produce a Salt event with the tag salt/netapi/hook/mycompany/myapp/mydata.
The following is an example .travis.yml file to send notifications to Salt of successful test runs:
language: python script: python -m unittest tests after_success: - | curl -sSk https://saltapi-url.example.com:8000/hook/travis/build/success                         -d branch="${TRAVIS_BRANCH}"                         -d commit="${TRAVIS_COMMIT}"
SEE ALSO: events, reactor
POST(*args, **kwargs) Fire an event in Salt with a custom event tag and data
POST /hook
• 413 -- request body is too large
curl -sS localhost:8000/hook \ -H 'Content-type: application/json' \ -d '{"foo": "Foo!", "bar": "Bar!"}'
POST /hook HTTP/1.1 Host: localhost:8000 Content-Length: 16 Content-Type: application/json
{"foo": "Foo!", "bar": "Bar!"}
HTTP/1.1 200 OK Content-Length: 14 Content-Type: application/json
{"success": true}
As a practical example, an internal continuous-integration build server could send an HTTP POST request to the URL https://localhost:8000/hook/mycompany/build/success which contains the result of a build and the SHA of the version that was built as JSON. That would then produce the following event in Salt that could be used to kick off a deployment via Salt's Reactor:
Event fired at Fri Feb 14 17:40:11 2014 ************************* Tag: salt/netapi/hook/mycompany/build/success Data: {'_stamp': '2014-02-14_17:40:11.440996', 'headers': { 'X-My-Secret-Key': 'F0fAgoQjIT@W', 'Content-Length': '37', 'Content-Type': 'application/json', 'Host': 'localhost:8000', 'Remote-Addr': '127.0.0.1'}, 'post': {'revision': 'aa22a3c4b2e7', 'result': True}}
Salt's Reactor could listen for the event:
reactor: - 'salt/netapi/hook/mycompany/build/*': - /srv/reactor/react_ci_builds.sls
And finally deploy the new build:
{% set secret_key = data.get('headers', {}).get('X-My-Secret-Key') %} {% set build = data.get('post', {}) %}
{% if secret_key == 'F0fAgoQjIT@W' and build.result == True %} deploy_my_app: cmd.state.sls: - tgt: 'application*' - arg: - myapp.deploy - kwarg: pillar: revision: {{ revision }} {% endif %}
/keys class salt.netapi.rest_cherrypy.app.Keys Convenience URLs for working with minion keys
These URLs wrap the functionality provided by the key wheel module functions.
GET(mid=None) Show the list of minion keys or detail on a specific key
GET /keys/(mid) List all keys or show a specific key
curl -i localhost:8000/keys
GET /keys HTTP/1.1 Host: localhost:8000 Accept: application/x-yaml
return: local: - master.pem - master.pub minions: - jerry minions_pre: [] minions_rejected: []
curl -i localhost:8000/keys/jerry
GET /keys/jerry HTTP/1.1 Host: localhost:8000 Accept: application/x-yaml
return: minions: jerry: 51:93:b3:d0:9f:3a:6d:e5:28:67:c2:4b:27:d6:cd:2b
POST(**kwargs) Easily generate keys for a minion and auto-accept the new key
Accepts all the same parameters as the key.gen_accept.
NOTE: A note about curl Avoid using the -i flag or HTTP headers will be written and produce an invalid tar file.
Example partial kickstart script to bootstrap a new minion:
%post mkdir -p /etc/salt/pki/minion curl -sSk https://localhost:8000/keys \ -d mid=jerry \ -d username=kickstart \ -d password=kickstart \ -d eauth=pam \ | tar -C /etc/salt/pki/minion -xf -
mkdir -p /etc/salt/minion.d printf 'master: 10.0.0.5\nid: jerry' > /etc/salt/minion.d/id.conf %end
POST /keys Generate a public and private key and return both as a tarball
Authentication credentials must be passed in the request.
curl -sSk https://localhost:8000/keys \ -d mid=jerry \ -d username=kickstart \ -d password=kickstart \ -d eauth=pam \ -o jerry-salt-keys.tar
POST /keys HTTP/1.1 Host: localhost:8000
HTTP/1.1 200 OK Content-Length: 10240 Content-Disposition: attachment; filename="saltkeys-jerry.tar" Content-Type: application/x-tar
jerry.pub0000644000000000000000000000070300000000000010730 0ustar  00000000000000
/ws class salt.netapi.rest_cherrypy.app.WebsocketEndpoint Open a WebSocket connection to Salt's event bus
The event bus on the Salt master exposes a large variety of things, notably when executions are started on the master and also when minions ultimately return their results. This URL provides a real-time window into a running Salt infrastructure. Uses websocket as the transport mechanism.
GET(token=None, **kwargs) Return a websocket connection of Salt's event stream
GET /ws/(token)
Query format_events The event stream will undergo server-side formatting if the format_events URL parameter is included in the request. This can be useful to avoid formatting on the client-side:
curl -NsS <...snip...> localhost:8000/ws?format_events
Reqheader X-Auth-Token an authentication token from Login.
Status 101 switching to the websockets protocol
Status 401 authentication required
Status 406 requested Content-Type not available
curl -NsSk \ -H 'X-Auth-Token: ffedf49d' \ -H 'Host: localhost:8000' \ -H 'Connection: Upgrade' \ -H 'Upgrade: websocket' \ -H 'Origin: https://localhost:8000' \ -H 'Sec-WebSocket-Version: 13' \ -H 'Sec-WebSocket-Key: '"$(echo -n $RANDOM | base64)" \ localhost:8000/ws
GET /ws HTTP/1.1 Connection: Upgrade Upgrade: websocket Host: localhost:8000 Origin: https://localhost:8000 Sec-WebSocket-Version: 13 Sec-WebSocket-Key: s65VsgHigh7v/Jcf4nXHnA== X-Auth-Token: ffedf49d
HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: mWZjBV9FCglzn1rIKJAxrTFlnJE= Sec-WebSocket-Version: 13
An authentication token may optionally be passed as part of the URL for browsers that cannot be configured to send the authentication header or cookie:
curl -NsS <...snip...> localhost:8000/ws/ffedf49d
// Note, you must be authenticated! var source = new Websocket('ws://localhost:8000/ws/d0ce6c1a'); source.onerror = function(e) { console.debug('error!', e); }; source.onmessage = function(e) { console.debug(e.data); };
source.send('websocket client ready')
source.close();
Or via Python, using the Python module websocket-client for example.
# Note, you must be authenticated!
from websocket import create_connection
ws = create_connection('ws://localhost:8000/ws/d0ce6c1a') ws.send('websocket client ready')
# Look at https://pypi.python.org/pypi/websocket-client/ for more # examples. while listening_to_events: print ws.recv()
ws.close()
Above examples show how to establish a websocket connection to Salt and activating real time updates from Salt's event stream by signaling websocket client ready.
/stats class salt.netapi.rest_cherrypy.app.Stats Expose statistics on the running CherryPy server
GET()  Return a dump of statistics collected from the CherryPy server
GET /stats
rest_tornado A non-blocking REST API for Salt depends
• tornado Python module
configuration All authentication is done through Salt's external auth system which requires additional configuration not described here.
In order to run rest_tornado with the salt-master add the following to the Salt master config file.
rest_tornado: # can be any port port: 8000 # address to bind to (defaults to 0.0.0.0) address: 0.0.0.0 # socket backlog backlog: 128 ssl_crt: /etc/pki/api/certs/server.crt # no need to specify ssl_key if cert and key # are in one single file ssl_key: /etc/pki/api/certs/server.key debug: False disable_ssl: False webhook_disable_auth: False cors_origin: null
Authentication Authentication is performed by passing a session token with each request. Tokens are generated via the SaltAuthHandler URL.
The token may be sent in one of two ways:
SEE ALSO: You can bypass the session handling via the RunSaltAPIHandler URL.
CORS rest_tornado supports Cross-site HTTP requests out of the box. It is by default deactivated and controlled by the cors_origin config key.
You can allow all origins by settings cors_origin to *.
You can allow only one origin with this configuration:
rest_tornado: cors_origin: http://salt.yourcompany.com
You can also be more specific and select only a few allowed origins by using a list. For example:
rest_tornado: cors_origin: - http://salt.yourcompany.com - http://salt-preprod.yourcampany.com
The format for origin are full URL, with both scheme and port if not standard.
In this case, rest_tornado will check if the Origin header is in the allowed list if it's the case allow the origin. Else it will returns nothing, effectively preventing the origin to make request.
For reference, CORS is a mechanism used by browser to allow (or disallow) requests made from browser from a different origin than salt-api. It's complementary to Authentication and mandatory only if you plan to use a salt client developed as a Javascript browser application.
Usage Commands are sent to a running Salt master via this module by sending HTTP requests to the URLs detailed below.
This REST interface is flexible in what data formats it will accept as well as what formats it will return (e.g., JSON, YAML, x-www-form-urlencoded).
Data sent in POST and PUT requests  must be in the format of a list of lowstate dictionaries. This allows multiple commands to be executed in a single HTTP request.
lowstate A dictionary containing various keys that instruct Salt which command to run, where that command lives, any parameters for that command, any authentication credentials, what returner to use, etc.
Salt uses the lowstate data format internally in many places to pass command data between functions. Salt also uses lowstate for the LocalClient() Python API interface.
The following example (in JSON format) causes Salt to execute two commands:
[{ "client": "local", "tgt": "*", "fun": "test.fib", "arg": ["10"] }, { "client": "runner", "fun": "jobs.lookup_jid", "jid": "20130603122505459265" }]
Multiple commands in a Salt API request will be executed in serial and makes no guarantees that all commands will run. Meaning that if test.fib (from the example above) had an exception, the API would still execute "jobs.lookup_jid".
Responses to these lowstates are an in-order list of dicts containing the return data, a yaml response could look like:
- ms-1: true ms-2: true - ms-1: foo ms-2: bar
In the event of an exception while executing a command the return for that lowstate will be a string, for example if no minions matched the first lowstate we would get a return like:
- No minions matched the target. No command was sent, no jid was assigned. - ms-1: true ms-2: true
Sending JSON or YAML in the request body is simple and most flexible, however sending data in urlencoded format is also supported with the caveats below. It is the default format for HTML forms, many JavaScript libraries, and the curl command.
For example, the equivalent to running salt '*' test.ping is sending fun=test.ping&arg&client=local&tgt=* in the HTTP request body.
Note, some popular frameworks and languages (notably jQuery, PHP, and Ruby on Rails) will automatically append empty brackets onto repeated parameters. E.g., arg=one, arg=two will be sent as arg[]=one, arg[]=two. This is not supported; send JSON or YAML instead.
A Websockets add-on to saltnado depends
In order to enable saltnado_websockets you must add websockets: True to your saltnado config block.
rest_tornado: # can be any port port: 8000 ssl_crt: /etc/pki/api/certs/server.crt # no need to specify ssl_key if cert and key # are in one single file ssl_key: /etc/pki/api/certs/server.key debug: False disable_ssl: False websockets: True
All Events Exposes all "real-time" events from Salt's event bus on a websocket connection.  It should be noted that "Real-time" here means these events are made available to the server as soon as any salt related action (changes to minions, new jobs etc) happens.  Clients are however assumed to be able to tolerate any network transport related latencies. Functionality provided by this endpoint is similar to the /events end point.
Exposes GET method to return websocket connections.  All requests should include an auth token.  A way to obtain obtain authentication tokens is shown below.
% curl -si localhost:8000/login \ -H "Accept: application/json" \ -d username='salt' \ -d password='salt' \ -d eauth='pam'
Which results in the response
{ "return": [{ "perms": [".*", "@runner", "@wheel"], "start": 1400556492.277421, "token": "d0ce6c1a37e99dcc0374392f272fe19c0090cca7", "expire": 1400599692.277422, "user": "salt", "eauth": "pam" }] }
In this example the token returned is d0ce6c1a37e99dcc0374392f272fe19c0090cca7 and can be included in subsequent websocket requests (as part of the URL).
// Note, you must be authenticated!
// Get the Websocket connection to Salt var source = new Websocket('wss://localhost:8000/all_events/d0ce6c1a37e99dcc0374392f272fe19c0090cca7');
// Get Salt's "real time" event stream. source.onopen = function() { source.send('websocket client ready'); };
// Other handlers source.onerror = function(e) { console.debug('error!', e); };
// e.data represents Salt's "real time" event data as serialized JSON. source.onmessage = function(e) { console.debug(e.data); };
// Terminates websocket connection and Salt's "real time" event stream on the server. source.close();
Or via Python, using the Python module websocket-client for example.  Or the tornado client.
# Get the Websocket connection to Salt ws = create_connection('wss://localhost:8000/all_events/d0ce6c1a37e99dcc0374392f272fe19c0090cca7')
# Get Salt's "real time" event stream. ws.send('websocket client ready')
# Simple listener to print results of Salt's "real time" event stream. # Look at https://pypi.python.org/pypi/websocket-client/ for more examples. while listening_to_events: print ws.recv()       #  Salt's "real time" event data as serialized JSON.
# Terminates websocket connection and Salt's "real time" event stream on the server. ws.close()
# Please refer to https://github.com/liris/websocket-client/issues/81 when using a self signed cert
Formatted Events Exposes formatted "real-time" events from Salt's event bus on a websocket connection.  It should be noted that "Real-time" here means these events are made available to the server as soon as any salt related action (changes to minions, new jobs etc) happens.  Clients are however assumed to be able to tolerate any network transport related latencies. Functionality provided by this endpoint is similar to the /events end point.
Formatted events parses the raw "real time" event stream and maintains a current view of the following:
• minions
• jobs
A change to the minions (such as addition, removal of keys or connection drops) or jobs is processed and clients are updated.  Since we use salt's presence events to track minions, please enable presence_events and set a small value for the loop_interval in the salt master config file.
// Get the Websocket connection to Salt var source = new Websocket('wss://localhost:8000/formatted_events/d0ce6c1a37e99dcc0374392f272fe19c0090cca7');
# Get the Websocket connection to Salt ws = create_connection('wss://localhost:8000/formatted_events/d0ce6c1a37e99dcc0374392f272fe19c0090cca7')
Example responses Minion information is a dictionary keyed by each connected minion's id (mid), grains information for each minion is also included.
Minion information is sent in response to the following minion events:
connection drops
• requires running manage.present periodically every loop_interval seconds
• minion addition
• minion removal
# Not all grains are shown data: { "minions": { "minion1": { "id": "minion1", "grains": { "kernel": "Darwin", "domain": "local", "zmqversion": "4.0.3", "kernelrelease": "13.2.0" } } } }
Job information is also tracked and delivered.
Job information is also a dictionary in which each job's information is keyed by salt's jid.
data: { "jobs": { "20140609153646699137": { "tgt_type": "glob", "jid": "20140609153646699137", "tgt": "*", "start_time": "2014-06-09T15:36:46.700315", "state": "complete", "fun": "test.ping", "minions": { "minion1": { "return": true, "retcode": 0, "success": true } } } } }
Setup REST URI Reference • /
• /login
• /minions
• /jobs
• /run
• /events
• /hook
/ class salt.netapi.rest_tornado.saltnado.SaltAPIHandler(application, request, **kwargs) Main API handler for base "/"
disbatch() Disbatch all lowstates to the appropriate clients
get()  An endpoint to determine salt-api capabilities
HTTP/1.1 200 OK Content-Type: application/json Content-Legnth: 83
{"clients": ["local", "local_async", "runner", "runner_async"], "return": "Welcome"}
post() Send one or more Salt commands (lowstates) in the request body
• X-Auth-Token -- a session token from SaltAuthHandler.
curl -si https://localhost:8000 \ -H "Accept: application/x-yaml" \ -H "X-Auth-Token: d40d1e1e" \ -d client=local \ -d tgt='*' \ -d fun='test.ping' \ -d arg
POST / HTTP/1.1 Host: localhost:8000 Accept: application/x-yaml X-Auth-Token: d40d1e1e Content-Length: 36 Content-Type: application/x-www-form-urlencoded
fun=test.ping&arg&client=local&tgt=*
Responses are an in-order list of the lowstate's return data. In the event of an exception running a command the return will be a string instead of a mapping.
Note that if multiple lowstate structures are sent, the Salt API will execute them in serial, and will not stop execution upon failure of a previous job. If you need to have commands executed in order and stop on failure please use compound-command-execution.
/login class salt.netapi.rest_tornado.saltnado.SaltAuthHandler(application, request, **kwargs) Handler for login requests
get()  All logins are done over post, this is a parked endpoint
GET /login
GET /login HTTP/1.1 Host: localhost:8000 Accept: application/json
HTTP/1.1 401 Unauthorized Content-Type: application/json Content-Length: 58
{"status": "401 Unauthorized", "return": "Please log in"}
post() Authenticate against Salt's eauth system
• 400 -- bad request
• 500 -- internal server error
curl -si localhost:8000/login \ -H "Accept: application/json" \ -d username='saltuser' \ -d password='saltpass' \ -d eauth='pam'
POST / HTTP/1.1 Host: localhost:8000 Content-Length: 42 Content-Type: application/x-www-form-urlencoded Accept: application/json
username=saltuser&password=saltpass&eauth=pam
/minions class salt.netapi.rest_tornado.saltnado.MinionSaltAPIHandler(application, request, **kwargs) A convenience endpoint for minion related functions
get(mid=None) A convenience URL for getting lists of minions or getting minion details
post() Start an execution command and immediately return the job id
lowstate data describing Salt commands must be sent in the request body. The client option will be set to local_async().
curl -sSi localhost:8000/minions \ -H "Accept: application/x-yaml" \ -d tgt='*' \ -d fun='status.diskusage'
POST /minions HTTP/1.1 Host: localhost:8000 Accept: application/x-yaml Content-Length: 26 Content-Type: application/x-www-form-urlencoded
return: - jid: '20130603122505459265' minions: [ms-4, ms-3, ms-2, ms-1, ms-0]
/jobs class salt.netapi.rest_tornado.saltnado.JobsSaltAPIHandler(application, request, **kwargs) A convenience endpoint for job cache data
get(jid=None) A convenience URL for getting lists of previously run jobs or getting the return from a single job
/run class salt.netapi.rest_tornado.saltnado.RunSaltAPIHandler(application, request, **kwargs) Endpoint to run commands without normal session handling
post() Run commands bypassing the normal session handling
POST /run This entry point is primarily for "one-off" commands. Each request must pass full Salt authentication credentials. Otherwise this URL is identical to the root URL (/).
curl -sS localhost:8000/run \ -H 'Accept: application/x-yaml' \ -d client='local' \ -d tgt='*' \ -d fun='test.ping' \ -d username='saltdev' \ -d password='saltdev' \ -d eauth='pam'
client=local&tgt=*&fun=test.ping&username=saltdev&password=saltdev&eauth=pam
/events class salt.netapi.rest_tornado.saltnado.EventsSaltAPIHandler(application, request, **kwargs) Expose the Salt event bus
get()  An HTTP stream of the Salt master event bus
retry: 400 data: {'tag': '', 'data': {'minions': ['ms-4', 'ms-3', 'ms-2', 'ms-1', 'ms-0']}}
data: {'tag': '20130802115730568475', 'data': {'jid': '20130802115730568475', 'return': True, 'retcode': 0, 'success': True, 'cmd': '_return', 'fun': 'test.ping', 'id': 'ms-1'}}
<!-- Note, you must be authenticated! --> var source = new EventSource('/events'); source.onopen = function() { console.debug('opening') }; source.onerror = function(e) { console.debug('error!', e) }; source.onmessage = function(e) { console.debug(e.data) };
var source = new EventSource('/events', {withCredentials: true});
Some browser clients lack CORS support for the EventSource() API. Such clients may instead pass the X-Auth-Token value as an URL parameter:
curl -NsS localhost:8000/events/6d1b722e
/hook class salt.netapi.rest_tornado.saltnado.WebhookSaltAPIHandler(application, request, **kwargs) A generic web hook entry point that fires an event on Salt's event bus
language: python script: python -m unittest tests after_success: - 'curl -sS http://saltapi-url.example.com:8000/hook/travis/build/success -d branch="${TRAVIS_BRANCH}" -d commit="${TRAVIS_COMMIT}"'
SEE ALSO: Events, Reactor
post(tag_suffix=None) Fire an event in Salt with a custom event tag and data
curl -sS localhost:8000/hook -d foo='Foo!' -d bar='Bar!'
POST /hook HTTP/1.1 Host: localhost:8000 Content-Length: 16 Content-Type: application/x-www-form-urlencoded
foo=Foo&bar=Bar!
As a practical example, an internal continuous-integration build server could send an HTTP POST request to the URL http://localhost:8000/hook/mycompany/build/success which contains the result of a build and the SHA of the version that was built as JSON. That would then produce the following event in Salt that could be used to kick off a deployment via Salt's Reactor:
rest_wsgi A minimalist REST API for Salt This rest_wsgi module provides a no-frills REST interface for sending commands to the Salt master. There are no dependencies.
Extra care must be taken when deploying this module into production. Please read this documentation in entirety.
All authentication is done through Salt's external auth system.
Usage • All requests must be sent to the root URL (/).
• All requests must be sent as a POST request with JSON content in the request body.
• All responses are in JSON.
SEE ALSO: rest_cherrypy
The rest_cherrypy module is more full-featured, production-ready, and has builtin security features.
Deployment The rest_wsgi netapi module is a standard Python WSGI app. It can be deployed one of two ways.
Using a WSGI-compliant web server This module may be run via any WSGI-compliant production server such as Apache with mod_wsgi or Nginx with FastCGI.
It is strongly recommended that this app be used with a server that supports HTTPS encryption since raw Salt authentication credentials must be sent with every request. Any apps that access Salt through this interface will need to manually manage authentication credentials (either username and password or a Salt token). Tread carefully.
salt-api using a development-only server If run directly via the salt-api daemon it uses the wsgiref.simple_server() that ships in the Python standard library. This is a single-threaded server that is intended for testing and development. This server does not use encryption; please note that raw Salt authentication credentials must be sent with every HTTP request.
Running this module via salt-api is not recommended!
In order to start this module via the salt-api daemon the following must be put into the Salt master config:
rest_wsgi: port: 8001
Usage examples POST / Example request for a basic test.ping:
% curl -sS -i \ -H 'Content-Type: application/json' \ -d '[{"eauth":"pam","username":"saltdev","password":"saltdev","client":"local","tgt":"*","fun":"test.ping"}]' localhost:8001
HTTP/1.0 200 OK Content-Length: 89 Content-Type: application/json
{"return": [{"ms--4": true, "ms--3": true, "ms--2": true, "ms--1": true, "ms--0": true}]}
Example request for an asynchronous test.ping:
% curl -sS -i \ -H 'Content-Type: application/json' \ -d '[{"eauth":"pam","username":"saltdev","password":"saltdev","client":"local_async","tgt":"*","fun":"test.ping"}]' localhost:8001
HTTP/1.0 200 OK Content-Length: 103 Content-Type: application/json
{"return": [{"jid": "20130412192112593739", "minions": ["ms--4", "ms--3", "ms--2", "ms--1", "ms--0"]}]}
Example request for looking up a job ID:
% curl -sS -i \ -H 'Content-Type: application/json' \ -d '[{"eauth":"pam","username":"saltdev","password":"saltdev","client":"runner","fun":"jobs.lookup_jid","jid":"20130412192112593739"}]' localhost:8001
form lowstate A list of lowstate data appropriate for the client interface you are calling.
status 200 success
status 401 authentication required
output modules Follow one of the below links for further information and examples
┌────────────────────┬─────────────────────────────┐ │dson                │ Display return data in DSON │ │                    │ format                      │ ├────────────────────┼─────────────────────────────┤ │highstate           │ Outputter for displaying    │ │                    │ results of state runs       │ ├────────────────────┼─────────────────────────────┤ │json_out            │ Display return data in JSON │ │                    │ format                      │ ├────────────────────┼─────────────────────────────┤ │key                 │ Display salt-key output     │ ├────────────────────┼─────────────────────────────┤ │nested              │ Recursively display nested  │ │                    │ data                        │ ├────────────────────┼─────────────────────────────┤ │newline_values_only │ Display values only,        │ │                    │ separated by newlines       │ ├────────────────────┼─────────────────────────────┤ │no_out_quiet        │ Display no output           │ ├────────────────────┼─────────────────────────────┤ │no_return           │ Display output for minions  │ │                    │ that did not return         │ ├────────────────────┼─────────────────────────────┤ │overstatestage      │ Display clean output of an  │ │                    │ overstate stage             │ ├────────────────────┼─────────────────────────────┤ │pony                │ Display Pony output data    │ │                    │ structure                   │ ├────────────────────┼─────────────────────────────┤ │pprint_out          │ Python pretty-print         │ │                    │ (pprint)                    │ ├────────────────────┼─────────────────────────────┤ │profile             │ Display profiling data in a │ │                    │ table format                │ ├────────────────────┼─────────────────────────────┤ │progress            │ Display return data as a    │ │                    │ progress bar                │ ├────────────────────┼─────────────────────────────┤ │raw                 │ Display raw output data     │ │                    │ structure                   │ ├────────────────────┼─────────────────────────────┤ │table_out           │ Display output in a table   │ │                    │ format                      │ ├────────────────────┼─────────────────────────────┤ │txt                 │ Simple text outputter       │ ├────────────────────┼─────────────────────────────┤ │virt_query          │ virt.query outputter        │ ├────────────────────┼─────────────────────────────┤ │yaml_out            │ Display return data in YAML │ │                    │ format                      │ └────────────────────┴─────────────────────────────┘
salt.output.dson Display return data in DSON format This outputter is intended for demonstration purposes. Information on the DSON spec can be found here.
This outputter requires Dogeon (installable via pip)
salt.output.dson.output(data, **kwargs) Print the output data in JSON
salt.output.highstate Outputter for displaying results of state runs The return data from the Highstate command is a standard data structure which is parsed by the highstate outputter to deliver a clean and readable set of information about the HighState run on minions.
Two configurations can be set to modify the highstate outputter. These values can be set in the master config to change the output of the salt command or set in the minion config to change the output of the salt-call command.
state_verbose By default state_verbose is set to True, setting this to False will instruct the highstate outputter to omit displaying anything in green, this means that nothing with a result of True and no changes will not be printed
state_output: The highstate outputter has six output modes, full, terse, mixed, changes and filter
• The default is set to full, which will display many lines of detailed information for each executed chunk.
• If terse is used, then the output is greatly simplified and shown in only one line.
• If mixed is used, then terse output will be used unless a state failed, in which case full output will be used.
• If changes is used, then terse output will be used if there was no error and no changes, otherwise full output will be used.
• If filter is used, then either or both of two different filters can be used: exclude or terse.
• for exclude, state.highstate expects a list of states to be excluded (or None) followed by True for terse output or False for regular output.  Because of parsing nuances, if only one of these is used, it must still contain a comma. For instance: exclude=True,.
• for terse, state.highstate expects simply True or False.
These can be set as such from the command line, or in the Salt config as state_output_exclude or state_output_terse, respectively.
The output modes have one modifier:
full_id, terse_id, mixed_id, changes_id and filter_id If _id is used, then the corresponding form will be used, but the value for name will be drawn from the state ID. This is useful for cases where the name value might be very long and hard to read.
state_tabular: If state_output uses the terse output, set this to True for an aligned output format.  If you wish to use a custom format, this can be set to a string.
If state_output: filter is set in the configuration file:
salt '*' state.highstate exclude=None,True
means to exclude no states from the highstate and turn on terse output.
salt twd state.highstate exclude=problemstate1,problemstate2,False
means to exclude states problemstate1 and problemstate2 from the highstate, and use regular output.
Example output for the above highstate call when top.sls defines only one other state to apply to minion twd:
twd:
Summary for twd ------------ Succeeded: 1 (changed=1) Failed:    0 ------------ Total states run:     1
Example output with no special settings in configuration files:
myminion: ---------- ID: test.ping Function: module.run Result: True Comment: Module function test.ping executed Changes: ---------- ret: True
Summary for myminion ------------ Succeeded: 1 Failed:    0 ------------ Total:     0
salt.output.highstate.output(data, **kwargs) The HighState Outputter is only meant to be used with the state.highstate function, or a function that returns highstate return data.
salt.output.json_out Display return data in JSON format configuration The output format can be configured in two ways: Using the --out-indent CLI flag and specifying a positive integer or a negative integer to group JSON from each minion to a single line.
Or setting the output_indent setting in the Master or Minion configuration file with one of the following values:
• Null: put each minion return on a single line.
• pretty: use four-space indents and sort the keys.
• An integer: specify the indentation level.
Salt's outputters operate on a per-minion basis. Each minion return will be output as a single JSON object once it comes in to the master.
Some JSON parsers can guess when an object ends and a new one begins but many can not. A good way to differentiate between each minion return is to use the single-line output format and to parse each line individually. Example output (truncated):
{"dave": {"en0": {"hwaddr": "02:b0:26:32:4c:69", ...}}} {"jerry": {"en0": {"hwaddr": "02:26:ab:0d:b9:0d", ...}}} {"kevin": {"en0": {"hwaddr": "02:6d:7f:ce:9f:ee", ...}}} {"mike": {"en0": {"hwaddr": "02:48:a2:4b:70:a0", ...}}} {"phill": {"en0": {"hwaddr": "02:1d:cc:a2:33:55", ...}}} {"stuart": {"en0": {"hwaddr": "02:9a:e0:ea:9e:3c", ...}}}
salt '*' foo.bar --out=json
salt.output.json_out.output(data, **kwargs) Print the output data in JSON
salt.output.key Display salt-key output The salt-key command makes use of this outputter to format its output.
salt.output.key.output(data, **kwargs) Read in the dict structure generated by the salt key API methods and print the structure.
salt.output.nested Recursively display nested data This is the default outputter for most execution functions.
myminion: ---------- foo: ---------- bar: baz dictionary: ---------- abc: 123 def: 456 list: - Hello - World
class salt.output.nested.NestDisplay(retcode=0) Manage the nested display contents
display(ret, indent, prefix, out) Recursively iterate down through data structures to determine output
ustring(indent, color, msg, prefix='', suffix='', endc=None)
salt.output.nested.output(ret, **kwargs) Display ret data
salt.output.newline_values_only Display values only, separated by newlines New in version 2015.5.0.
This outputter is designed for Salt CLI return data. It will do the following to the return dict:
1. Get just the values (ignoring the minion IDs).
2. Each value, if it is iterable, is split a separate line.
3. Each minion's values are separated by newlines.
This results in a single string of return data containing all the values from the various minions.
WARNING: As noted above, this outputter will discard the minion ID. If the minion ID is important, then an outputter that returns the full return dictionary in a parsable format (such as json, pprint,, or yaml) may be more suitable.
Example 1 salt '*' foo.bar --out=newline_values_only
Input { 'myminion': ['127.0.0.1', '10.0.0.1'], 'second-minion': ['127.0.0.1', '10.0.0.2'] }
Output 127.0.0.1 10.0.0.1 127.0.0.1 10.0.0.2
Example 2 salt '*' foo.bar --out=newline_values_only
Input { 'myminion': 8, 'second-minion': 10 }
Output 8 10
salt.output.newline_values_only.output(data, **kwargs) Display modified ret data
salt.output.no_out_quiet Display no output No output is produced when this outputter is selected
salt '*' foo.bar --out=quiet
salt.output.no_out_quiet.output(ret, **kwargs) Don't display data. Used when you only are interested in the return.
salt.output.no_return Display output for minions that did not return This outputter is used to display notices about which minions failed to return when a salt function is run with -v or --verbose. It should not be called directly from the CLI.
virtucentos: Minion did not return
class salt.output.no_return.NestDisplay Create generator for nested output
salt.output.no_return.output(ret, **kwargs) Display ret data
salt.output.overstatestage Display clean output of an overstate stage This outputter is used to display Orchestrate Runner stages, and should not be called directly.
salt.output.overstatestage.output(data, **kwargs) Format the data for printing stage information from the overstate system
salt.output.pony Display Pony output data structure depends
• ponysay CLI program
Display output from a pony. Ponies are better than cows because everybody wants a pony.