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:
Include a custom header named X-Auth-Token.
Sent via a cookie. This option is a convenience for HTTP clients that automatically handle cookie support (such as browsers).
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.
Content negotiation
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).
Specify the format of data in the request body by including the Content-Type header.
Specify the desired data format for the response body with the Accept header.
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
x-www-form-urlencoded
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.
Caveats:
Only a single command may be sent per HTTP request.
-
Repeating the
arg
parameter multiple times will cause those parameters to be combined into a single list.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 asarg[]=one
,arg[]=two
. This is not supported; send JSON or YAML instead.
A Websockets add-on to saltnado
- depends
-
tornado Python module
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.
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.
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).
The event stream can be easily consumed via JavaScript:
// 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.
# Note, you must be authenticated! from websocket import create_connection # 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
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
.
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.
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.
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.
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).
The event stream can be easily consumed via JavaScript:
// Note, you must be authenticated! // Get the Websocket connection to Salt var source = new Websocket('wss://localhost:8000/formatted_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.
# Note, you must be authenticated! from websocket import create_connection # Get the Websocket connection to Salt ws = create_connection('wss://localhost:8000/formatted_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
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
.
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 everyloop_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
© 2021 SaltStack.
Licensed under the Apache License, Version 2.0.
https://docs.saltproject.io/en/latest/ref/netapi/all/salt.netapi.rest_tornado.html