截止当前,cherrypy的REST API方案是最为成熟的,也是Salt官方更为推荐使用的一个REST API实现方案。
您也可以参考在Github上维护的这一份技术资料:rest_cherrypy
depends - 必需依赖
CherryPy Python module.
注意: 有一个已知的 SSL traceback 功能bug存在于 CherryPy versions 3.2.5 到 3.7.x之间的版本中,请使用版本 3.2.3 或者是最新的 10.x 版本。
optdepends - 可选依赖
client_libraries - 客户端工具
setup - 安装 REST_CHERRYPY
以下所有步骤均在运行Salt Master守护程序的计算机上执行。 配置则整合进了master配置文件中。
salt-api
。 (此步骤在OS和Linux发行版之间有所不同。某些软件包系统有一个分离的软件包,其他软件包系统在主Salt软件包中包含salt-api。确保salt-api --version
输出与salt --version
输出信息相匹配。) salt-call --local tls.create_self_signed_cert
rest_cherrypy
模块。 (根据需要调整证书路径,或禁用SSL(不推荐!)。 rest_cherrypy:
port: 8000
ssl_crt: /etc/pki/tls/certs/localhost.crt
ssl_key: /etc/pki/tls/certs/localhost.key
salt-master
服务进程。salt-api
服务进程。configuration - 配置 REST_CHERRYPY
下面详细介绍了所有可用的配置选项。 这些设置用于配置CherryPy HTTP服务器,在使用外部服务器(例如Apache或Nginx)时不适用。
port
必需配置项。指定webserver的服务端口。
host 0.0.0.0
HTTP服务器侦听的套接字接口。
debug False
在开发模式下启动Web服务器。 当底层代码更改时,它将重新加载自身,并输出更多的调试信息。
log_access_file
写入HTTP访问日志的文件的路径。
New in version 2016.11.0.
log_error_file
写入HTTP错误日志的文件的路径。
New in version 2016.11.0.
ssl_crt
定义 SSL 证书文件的路径。 (See below)
ssl_key
定义SSL certificate 证书的私钥文件的路径。 (See below)
ssl_chain
(使用PyOpenSSL时是可选的)传递给Context.load_verify_locationsr 的证书链。
disable_ssl
一个禁用SSL的配置项。警告: 你的 Salt 认证信息将会以明文发送!
webhook_disable_auth False
Webhook URL默认情况下需要身份验证,但是不能始终将外部服务配置为发送身份验证。 请参阅下文中的Webhook文档以获取有关保护此接口的建议。
webhook_url/hook
配置Webhook入口点的URL端点。
thread_pool 100
定义在线程池中使用的并发线程数。
socket_queue_size 30
指定HTTP连接队列的最大值。
expire_responses True
是否检查并杀死超过默认超时时间的HTTP响应。
自2016.11.9,2017.7.3,2018.3.0版本以来已弃用:CherryPy中的expire_responses
功能上等同于CherryPy中的timeout_monitor
参数,在>=12.0.0的CherryPy版本中不再受支持。
max_request_body_size 1048576
HTTP request 请求允许携带的最大数据。
collect_stats False
检查和报告CherryPy服务器的统计数据。
报告可以通过访问 /stats
URL端点地址获取。
stats_disable_auth False
访问 /stats
端点时不需要提供访问认证信息。
New in version 2018.3.0.
static
静态HTML/JavaScript/CSS/image资产的文件系统路径。
static_path /static
访问static
设置中指定的目录中提供静态资产时使用的URL前缀。
enable_sessions True
启用或禁用所有依赖会话cookie的端点。 这对于执行仅基于headers的身份验证可能很有用。
New in version 2017.7.0.
app index.html
HTML文件的文件系统路径,它将用作静态文件。 这对于引导单页JavaScript应用程序很有用。
警告! 如果将此选项设置为自定义Web应用程序,则使用基于cookie的身份验证的任何内容都容易受到XSRF攻击。 建议发送自定义X-Auth-Token
标头,并考虑禁用enable_sessions
设置。
Changed in version 2017.7.0: 增加一个 proof-of-concept JavaScript 单页应用。
app_path /app
用于提供在应用程序设置中指定的HTML文件的URL前缀。 这应该是一个不包含斜杠的简单名称。
指定路径之后的所有路径信息都将被忽略; 这对于使用HTML5历史记录API的应用程序很有用。
root_prefix /
指向应用程序主入口点的URL路径。 这对于从同一URL提供多个应用程序很有用。
通过向每个请求传递会话令牌来执行认证的操作。 令牌是通过名为Login的URL生成的。
令牌可以通过以下两种方式之一发送:作为自定义header或作为会话cookie。 对于支持cookie的客户端,后者要方便得多。
包括一个名为X-Auth-Token
属性的自定义header。
例如,使用curl:
curl -sSk https://localhost:8000/login \
-H 'Accept: application/x-yaml' \
-d username=saltdev \
-d password=saltdev \
-d eauth=pam
从输出中复制token
令牌值,并将其包含在后续的请求操作中:
curl -sSk https://localhost:8000 \
-H 'Accept: application/x-yaml' \
-H 'X-Auth-Token: 697adbdc8fe971d09ae4c2a3add7248859c87079'\
-d client=local \
-d tgt='*' \
-d fun=test.ping
通过Cookie发送。 对于可以自动处理cookie支持的HTTP客户端(例如浏览器),此选项会很方便。
例如,使用curl:
# Write the cookie file:
curl -sSk https://localhost:8000/login \
-c ~/cookies.txt \
-H 'Accept: application/x-yaml' \
-d username=saltdev \
-d password=saltdev \
-d eauth=auto
# Read the cookie file:
curl -sSk https://localhost:8000 \
-b ~/cookies.txt \
-H 'Accept: application/x-yaml' \
-d client=local \
-d tgt='*' \
-d fun=test.ping
另外一个在Python中使用 requests类库的例子:
>>> import requests
>>> session = requests.Session()
>>> session.post('http://localhost:8000/login', json={
'username': 'saltdev',
'password': 'saltdev',
'eauth': 'auto',
})
<Response [200]>
>>> resp = session.post('http://localhost:8000', json=[{
'client': 'local',
'tgt': '*',
'fun': 'test.arg',
'arg': ['foo', 'bar'],
'kwarg': {'baz': 'Baz!'},
}])
>>> resp.json()
{u'return': [{
...snip...
}]}
See also:你还可以使用 Run URL来传递session会话数据,作为POST数据中的一项键值。
该接口直接公开作为Salt的Python API使用。 CLI可用的一切功能也都可以通过Python API实现。 命令是在Salt Master上执行。
根URL(/
)类似于RPC,因为它在请求正文中接受有关要执行哪些Salt函数的指令,并且在响应中包含这些函数调用的结果。
例如:
% curl -sSi https://localhost:8000 -H 'Content-type: application/json' -d '[{
"client": "local",
"tgt": "*",
"fun": "test.ping"
}]'
HTTP/1.1 200 OK
Content-Type: application/json
[...snip...]
{"return": [{"jerry": true}]}
请求主体必须是命令数组。 使用下面这样的工作流程来构建命令:
client
字段是对Salt的Python API中使用的主要Python类的引用。详情可以阅读完整的客户端API文档,但简而言之:
salt
CLI命令。salt-run
CLI命令。salt-key
CLI命令相似的管理功能。大多数客户端具有诸如同步执行或异步执行之类的变体,以及诸如批处理执行之类的其他变体。请参阅客户端接口的完整列表。
每个客户端需要不同的参数,有时具有不同的语法。例如,LocalClient
需要tgt
参数,因为它将命令转发给Minions,而其他客户端接口则不需要。 LocalClient
还使用arg
(数组)和kwarg
(字典)参数,因为这些值也需要被发送到Minions,并用于在那里执行请求的功能。 RunnerClient
和WheelClient
直接在Master服务器上执行,因此不需要或不接受这些参数。
阅读上面链接的客户端文档中的方法签名,但是希望有一个示例可以帮助说明该概念。这个例子使用Salt执行两个函数-使用LocalClient执行的test.arg execution函数和使用RunnerClient执行的test.arg Runner函数;注意每个命令的结构不同。两者的结果合并并作为一个响应返回。
% curl -b ~/cookies.txt -sSi localhost:8000 -H 'Content-type: application/json' -d '
[
{
"client": "local",
"tgt": "*",
"fun": "test.arg",
"arg": ["positional arg one", "positional arg two"],
"kwarg": {
"keyword arg one": "Hello from a minion",
"keyword arg two": "Hello again from a minion"
}
},
{
"client": "runner",
"fun": "test.arg",
"keyword arg one": "Hello from a master",
"keyword arg two": "Runners do not support positional args"
}
]
'
HTTP/1.1 200 OK
[...snip...]
{
"return": [
{
"jerry": {
"args": [
"positional arg one",
"positional arg two"
],
"kwargs": {
"keyword arg one": "Hello from a minion",
"keyword arg two": "Hello again from a minion",
[...snip...]
}
},
[...snip; other minion returns here...]
},
{
"args": [],
"kwargs": {
"keyword arg two": "Runners do not support positional args",
"keyword arg one": "Hello from a master"
}
}
]
}
另一个例子, 这次使用更常见的功能函数:
curl -b /tmp/cookies.txt -sSi localhost:8000 -H 'Content-type: application/json' -d '
[
{
"client": "local",
"tgt": "*",
"fun": "state.sls",
"kwarg": {
"mods": "apache",
"pillar": {
"lookup": {
"wwwdir": "/srv/httpd/htdocs"
}
}
}
},
{
"client": "runner",
"fun": "cloud.create",
"provider": "my-ec2-provider",
"instances": "my-centos-6",
"image": "ami-1624987f",
"delvol_on_destroy", true
}
]
'
HTTP/1.1 200 OK
[...snip...]
{
"return": [
{
"jerry": {
"pkg_|-install_apache_|-httpd_|-installed": {
[...snip full state return here...]
}
}
[...snip other minion returns here...]
},
{
[...snip full salt-cloud output here...]
}
]
}
这个REST接口可以灵活接受各种数据格式,以及可以传回的格式(例如JSON,YAML和urlencoded)。
对于大多数HTTP请求,我们建议使用JSON格式。 urlencoded数据很简单,不能表示复杂的数据结构 - 而这对于某些Salt命令来说通常是必需的,例如启动使用Pillar数据的状态运行。 Salt的CLI工具可以将在CLI处传递的字符串重新格式化为复杂的数据结构,并且该行为也可以通过salt-api起作用,但这可能很脆弱。因为salt-api可以接受JSON,所以最好仅发送JSON。
下面是发送urlencoded数据的示例:
curl -sSik https://localhost:8000 \
-b ~/cookies.txt \
-d client=runner \
-d fun='jobs.lookup_jid' \
-d jid='20150129182456704682'
注意:urlencoded数据警告
每个HTTP请求只能发送一个命令。
多次重复arg
参数将导致这些参数组合到一个列表中。
注意,一些流行的框架和语言(特别是jQuery,PHP和Ruby on Rails)会自动将空括号附加到重复的查询字符串参数上。 例如,?foo[]=fooone&foo[]=footwo
。,而这是不支持的。请发送?foo=fooone&foo=footwo
,或者发送JSON或YAML。
关于使用curl
的注意事项
curl的-d
标志不会自动对可能影响密码和包含必须编码字符的其他数据的数据进行urlencode。 请改用--data-urlencode
标志。 例如。:
curl -ksi http://localhost:8000/login \
-H "Accept: application/json" \
-d username='myapiuser' \
--data-urlencode password='1234+' \
-d eauth='pam'
性能期望和推荐用法
该模块提供了Salt的Python API的精简包装。 通过rest_cherrypy执行Salt命令直接类似于通过Salt的CLI(也使用Python API)执行Salt命令-它们共享相同的语义、性能特征以及98%的相同代码。 作为一个经验法则:如果您不打算在CLI上执行此操作,请也不要通过此API进行操作。
CherryPy服务器是用Python编写的可用于生产环境的线程HTTP服务器。 因为它利用线程池来处理HTTP请求,所以它不适合维护大量并发的同步连接。 在具有默认设置的中型硬件设备上,它应该在大约30到50个并发连接时达到性能顶峰。
这么长时间运行的Salt同步过程也不理想。 像在CLI中一样,每次Salt命令运行都会启动一个实例化自己的LocalClient
的进程,该进程实例化自己对Salt事件总线的侦听器,并发出自己的定期saltutil.find_job
查询以确定Minion是否仍在运行该命令 。 不完全是轻量级的操作。
除了上述用于长时间运行的连接的资源开销外,CherryPy服务器,使用的任何HTTP客户端以及介于两者之间的任何硬件(例如代理、网关或负载均衡器)都有常用的HTTP超时语义。 可以将rest_cherrypy配置为不通过expire_responses
设置使长响应超时,并且LocalClient和RunnerClient都有自己的超时参数,可以将其作为顶级关键字传递:
curl -b /tmp/cookies.txt -sSi localhost:8000 -H 'Content-type: application/json' -d '
[
{
"client": "local",
"tgt": "*",
"fun": "test.sleep",
"kwarg": {"length": 30},
"timeout": 60
},
{
"client": "runner",
"fun": "test.sleep",
"kwarg": {"s_time": 30},
"timeout": 60
}
]
'
考虑到上述长时间运行的操作的性能开销和HTTP超时,使用Salt和salt-api的最有效、最可扩展的方法是使用local_async
,runner_async
和wheel_async
客户端异步运行命令。
运行异步作业可以使LocalClient
每秒处理的命令数量增加3倍
,而RunnerClient
每秒处理的命令数量增加17倍
,此外,网络流量和内存需求也要少得多。可以通过/jobs/<jid>
端点从Salt的作业缓存中获取作业返回,或者可以使用Salt的Returner
系统将其收集到数据存储中。
/events
端点专门用于处理长时间运行的HTTP连接,它公开了Salt的事件总线,其中包括作业返回。首先观看此端点,然后再执行异步Salt命令,是使用rest_cherrypy
的最轻便和可扩展的方式,同时仍实时接收作业返回。但这要求客户端能够正确处理该工作流的固有异步性。
可以使用thread_pool
和socket_queue_size
设置来增加rest_cherrypy处理传入请求的能力。测试这些设置的更改时,请密切注意RAM使用情况以及可用的文件句柄。由于salt-api是Salt的Python API的简单包装,因此在测试时也请注意Salt的性能。
既然Salt已经在内部使用Tornado并发库,我们计划通过利用现有流程和事件侦听器来提高API的性能,并使用轻量级协程来促进更多同时HTTP连接和更好地支持同步操作。可以在 issue 26505 中跟踪该工作,但是在该问题解决之前,rest_cherrypy将仍然是官方推荐的REST API。
部署方式的说明
rest_cherrypy
netapi模块是标准的Python WSGI应用程序。 可以采用以下两种方法之一进行部署。
默认配置是在Salt master节点上面使用salt-api运行此模块以启动基于Python的CherryPy服务器。 该服务器是轻量级的、多线程的、使用SSL加密的,应该被认为可以投入生产。 有关性能预期,请参见以上部分。
该模块可以部署在任何与WSGI兼容的服务器上,例如具有mod_wsgi的Apache或具有FastCGI的Nginx,仅举两个(有很多)。
注意,外部WSGI服务器直接处理URL、路径和SSL证书。 rest_cherrypy
配置选项将被忽略,并且salt-api
守护程序根本不需要运行。 请记住,除非强制执行SSL,否则Salt身份验证凭据将以明文形式发送!
Apache虚拟主机的配置示例:
<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>
Salt CherryPy REST URI接口参考资料
class salt.netapi.rest_cherrypy.app.LowDataAdapter
这是Salt的REST API接口的主要入口端点。
下面是关于该API的使用说明。
GET /
request请求的使用示例:
curl -i localhost:8000
GET / HTTP/1.1
Host: localhost:8000
Accept: application/json
response响应:
Content-Type: application/json
class salt.netapi.rest_cherrypy.app.Login(*args, **kwargs)
登录并获取一个会话令牌。参数使用方法请参考 Authentication information 。
显示登录接口令牌。
GET /login
有关如何登录的说明。
request请求的使用示例:
curl -i localhost:8000/login
GET /login HTTP/1.1
Host: localhost:8000
Accept: text/html
response响应:
HTTP/1.1 200 OK
Content-Type: text/html
针基于Salt的eauth系统进行身份验证。
POST /login
request请求的使用示例:
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"}
response响应:
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.*"
]
}}
class salt.netapi.rest_cherrypy.app.Logout
清除或使会话令牌失效的工具类。
销毁当前活动的会话并使会话cookie过期
class salt.netapi.rest_cherrypy.app.Minions
可以用于对minions进行配置管理的URLs。
可用于获取minions列表或获取minion详细信息的便捷URL。
GET /minions/(mid)
request请求的使用示例:
curl -i localhost:8000/minions/ms-3
GET /minions/ms-3 HTTP/1.1
Host: localhost:8000
Accept: application/x-yaml
response响应:
HTTP/1.1 200 OK
Content-Length: 129005
Content-Type: application/x-yaml
return:
- ms-3:
grains.items:
...
启动一个执行命令并立即返回job id 。
POST /minions
200 – success
400 – bad or malformed request
401 – authentication required
406 – requested Content-Type not available
描述Salt命令内容的lowstate
数据必须在请求正文中发送。 client
选项将被设置为local_async()
。
request请求的使用示例:
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
response响应:
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
class salt.netapi.rest_cherrypy.app.Jobs
一个方便使用的URL,用于获取先前运行的作业的列表或从单个作业中获取返回数据。
GET /jobs/(jid)
列出作业列表或显示作业缓存中的单个作业的详细信息。
request请求的使用示例:
curl -i localhost:8000/jobs
GET /jobs HTTP/1.1
Host: localhost:8000
Accept: application/x-yaml
response响应:
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
request请求的使用示例:
curl -i localhost:8000/jobs/20121130104633606931
GET /jobs/20121130104633606931 HTTP/1.1
Host: localhost:8000
Accept: application/x-yaml
response响应:
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
class salt.netapi.rest_cherrypy.app.Run
绕过常规的session处理,运行管理命令。
salt-api不执行授权,是由Salt的eauth系统执行授权。 Local/Runner/WheelClient都可以接受username
/password
/eauth
或者 token
kwargs ,然后交由eauth系统执行检查。 rest_cherrypy
中的会话机制仅将会话与Salt eauth令牌配对,然后自动将Token
kwarg传入。
如果您已经有一个Salt eauth token了(可能是由Auth Runner模块中的mk_token函数生成的),则没有理由再使用sessions。
该URL端点接受用使用username, password, eauth这三个参数,或者是一个 token
kwarg关键字参数,并且根本不使用sessions。
绕过常规的session处理,运行管理命令。除此之外,该URL端点与root URL (/)相同。
POST /run
描述Salt命令的lowstate数据数组必须在请求正文中发送。
request请求的使用示例:
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"
}]'
或者是使用 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"}]
response响应:
HTTP/1.1 200 OK
Content-Length: 73
Content-Type: application/x-yaml
return:
- ms-0: true
ms-1: true
ms-2: true
ms-3: true
ms-4: true
/run
enpoint也可以用于使用salt-ssh
子系统执行命令。
使用salt-ssh时,不应提供eauth认证凭据。 相反地,身份验证应由SSH层本身处理。 使用salt-ssh客户端时也不需要运行Salt Master。 相反,Salt配置目录中只能存在一个roster名册文件。
所有SSH客户端请求都是同步执行的。
SSH客户端请求的示例:
curl -sS localhost:8000/run \
-H 'Accept: application/x-yaml' \
-d client='ssh' \
-d tgt='*' \
-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
client=ssh&tgt=*&fun=test.ping
SSH response响应:
return:
- silver:
fun: test.ping
fun_args: []
id: silver
jid: '20141203103525666185'
retcode: 0
return: true
success: true
class salt.netapi.rest_cherrypy.app.Events
对外暴露Salt的事件总线。
Salt master主机上的事件总线对外暴露了各种各样的东西,特别是在master主机上开始执行时,以及在minions最终返回其结果时。 该URL提供了运行中的Salt基础结构的一个实时信息窗口。
See also: Events & Reactor
Salt master事件总线的一个HTTP流。
该流按照服务器发送事件(SSE)规范进行格式化。 每个事件的格式均为JSON。
GET /events
curl -NsS localhost:8000/events?token=308650d
curl -NsS localhost:8000/events?salt_token=30742765
request请求示例:
curl -NsS localhost:8000/events
GET /events HTTP/1.1
Host: localhost:8000
response响应:
注意,tag
字段不是规范的一部分。 符合SSE的客户端应忽略未知字段。 此添加使不兼容的客户端仅监视某些标签,而不必每次都反序列化JSON对象。
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'}}
这个事件流可以容易得使用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);
};
请注意,SSE流是快速且完全异步的,而Salt是非常快的。如果使用常规POST请求创建了作业,则在POST请求的响应到达之前,作业返回将在SSE流上可用。在设计应用程序时,必须考虑到异步性。以下是一些一般准则。
- 在创建任何事件前就订阅SSE流。
- 在SSE事件到达时直接对其进行处理,而无需等待任何其他过程首先“完成”(例如ajax请求)。
- 如果必须将事件流用于同步查找,请保留事件的缓冲区。
- 将Salt的事件流直接写到DOM时要谨慎。它非常忙,可以迅速使分配给浏览器选项卡的内存不堪重负。
该文件旁边提供了一个完整的有效概念验证目的的JavaScript应用程序。可以通过将浏览器指向正在运行的rest_cherrypy
实例中的/app
端点来查看它。
或使用CORS:
var source = new EventSource('/events?token=ecd589e4e01912cf3c4035afad73426dbb8dba75', {withCredentials: true});
也可以通过shell消费流。
记录由空白行分隔; 在尝试反序列化JSON之前,需要手动删除data:
和tag:
前缀。
curl的 -N
标识关闭了输入缓存功能,因为我们需要能增量的处理流。
这是打印每个事件的一个基本示例:
curl -NsS localhost:8000/events |\
while IFS= read -r line ; do
echo $line
done
下面是使用 awk 基于 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"}}
class salt.netapi.rest_cherrypy.app.Webhook
一个通用的Web hook入口点,可在Salt的事件总线上触发事件。
外部服务可以将数据发布到此URL,以触发Salt中的相应事件。例如,Amazon SNS、Jenkins-CI或Travis-CI或GitHub Web hooks。
注意安全性
Salt的Reactor可以运行任何代码。响应钩子事件的Reactor SLS负责验证该事件来自受信任的源并包含有效数据。
这是一个通用接口,如何确保安全由您决定!
此URL需要身份验证,但是并非所有外部服务都可以配置为进行身份验证。因此,可以有选择地禁用此URL的身份验证。建议遵循最佳做法-始终使用SSL、传递密钥、将防火墙配置为仅允许来自已知来源的流量等。
事件数据取自请求的正文。Content-Type header适用于有效负载。
事件标记的前缀是`salt/netapi/hook`,URL路径附加在末尾。例如,发送到`/hook/mycompany/myapp/mydata`的`POST`请求将产生一个带有`Salt/netapi/hook/mycompany/myapp/mydata`标签的Salt事件。
以下是一个示例`.travis.yml`文件,用于将成功运行测试的通知发送给Salt:
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, reactor
使用自定义的标签和数据,在Salt中触发一个事件。
POST /hook
request请求使用示例:
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!"}
response响应示例:
HTTP/1.1 200 OK
Content-Length: 14
Content-Type: application/json
{"success": true}
作为一个实际可以运行的示例,内部的持续集成构建服务器可以将HTTP POST请求发送到URL https://localhost:8000/hook/mycompany/build/success,其中包含构建结果和该版本的SHA被构建为JSON。 然后,这将在Salt中产生以下事件,该事件可用于通过Salt的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 的 Reactor 响应器将会监控这类事件:
reactor:
- 'salt/netapi/hook/mycompany/build/*':
- /srv/reactor/react_ci_builds.sls
并最终完成应用程序的部署工作:
{% 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 %}
class salt.netapi.rest_cherrypy.app.Keys
用于管理minion keys的便捷URLs。
New in version 2014.7.0.
这些URL包含了 key wheel module 功能提供的功能。
显示minion keys列表或特定key的详细信息。
*New in version 2014.7.0.*
GET /keys/(mid)
request请求使用示例:
curl -i localhost:8000/keys
GET /keys HTTP/1.1
Host: localhost:8000
Accept: application/x-yaml
response响应示例:
HTTP/1.1 200 OK
Content-Length: 165
Content-Type: application/x-yaml
return:
local:
- master.pem
- master.pub
minions:
- jerry
minions_pre: []
minions_rejected: []
request请求使用示例:
curl -i localhost:8000/keys/jerry
GET /keys/jerry HTTP/1.1
Host: localhost:8000
Accept: application/x-yaml
response响应示例:
HTTP/1.1 200 OK
Content-Length: 73
Content-Type: application/x-yaml
return:
minions:
jerry: 51:93:b3:d0:9f:3a:6d:e5:28:67:c2:4b:27:d6:cd:2b
可以方便的为minion生成密钥并自动接受新密钥。
接受与key.gen_accept相同的所有参数。
注意:关于
curl
的使用请避免-i
标志,否则将会也写入HTTP header数据,以致于生成无效的tar文件。
下面是在kickstart脚本中用来引导和配置新的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
生成公钥和私钥,并打包为一个tar压缩包文件返回。
身份验证凭据必须在请求中传递。
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
response响应示例:
HTTP/1.1 200 OK
Content-Length: 10240
Content-Disposition: attachment; filename="saltkeys-jerry.tar"
Content-Type: application/x-tar
jerry.pub0000644000000000000000000000070300000000000010730 0ustar 00000000000000
class salt.netapi.rest_cherrypy.app.WebsocketEndpoint
打开一个与Salt的事件总线的WebSocket连接。
Salt master主机上的事件总线公开了各种各样的东西,特别是在master主机上开始执行时,以及在minions最终返回其结果时。 该URL提供了运行中的Salt基础结构的实时令牌窗口。 使用websocket作为传输机制。
See also:Events & Reactor
返回一个与Salt的事件总线的WebSocket连接。
GET /ws/(token)
Query format_events
如果请求中包含format_events
的URL参数,则事件流将进行服务器端进行内容格式化。 这对于需要避免在客户端进行格式化时很有用: curl -NsS <...snip...> localhost:8000/ws?format_events
Reqheader X-Auth-Token
一个在Login步骤中获取的身份认证token。
Status 101
切换至websockets协议。
Status 401
需要通过身份验证。
Status 406
request请求中的Content-Type无效。
request请求的使用示例:
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
response响应的示例:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: mWZjBV9FCglzn1rIKJAxrTFlnJE=
Sec-WebSocket-Version: 13
对于无法配置为发送的请求携带身份验证header或cookie的浏览器客户端,可以选择性地将身份验证令牌作为URL的一部分传递: curl -NsS <...snip...> localhost:8000/ws/ffedf49d
可以很容易地使用JavaScript解析处理事件流数据:
// 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();
或者使用Python。例如,使用 Python 模块 websocket-client 。
# 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()
上面的示例显示了如何建立与Salt的websocket连接以及如何通过向websocket客户端发送信号通知激活Salt的事件流的实时更新。
class salt.netapi.rest_cherrypy.app.Stats
对外暴露 CherryPy server 的运行统计数据。
返回从CherryPy服务器收集的统计信息的转储。
GET /stats