api 接口分为 2 类
Python client API - SaltStack Python 客户端程序 API 开发
netapi modules - SaltStack netapi 模块开发指南
Python client API
Salt 提供了几个与 Python 应用程序交互的接口。 这些接口通常称为 *Client() APIs。 每个客户端都可以从 master 服务器或 minion 服务器访问 Salt 的不同服务。 每类 Client API 的详细使用信息都会在下文进行说明。可以使用 Ipython 运行下列代码,输入命令 ipython3 启动。
加载 salt 配置文件
加载 salt master 配置文件
import salt.config
master_opts = salt.config.client_config('/etc/salt/master')
print(master_opts)
返回 Salt Master 配置文件的字典,其中包含与在本地运行的 Salt Master 守护程序进行通信所需的必要选项。 此功能可以为客户端搜索特定的配置,并将其添加到 master 配置的数据中。
加载 salt minion 配置文件
import salt.config
minion_opts = salt.config.minion_config('/etc/salt/minion')
print(minion_opts)
Salt Loader Interface 加载器接口
Salt 运行环境中的模块是通过特定的加载程序系统加载到内存中的。 这使得模块有使用条件要求(操作系统、操作系统版本、安装的库等),并允许 Salt 注入特殊变量(salt,opts等)。
大多数模块可以手动加载。 这在第三方 Python 应用程序或编写测试时通常很有用。 但是,某些模块要求并期望其下层系统具有完整的、可运行的 Salt 系统。 值得注意的是需要从 master 到 minion 进行通信的模块,如 mine, publish 以及 peer 模块。 错误信息 KeyError: 'master_uri' 可能是这种情况的指示。 在这些情况下时,请使用 Caller 类执行这些模块。
Salt 中的每种模块类型都有相应的加载器函数。
加载执行模块 minion_mods
通过评估每个模块中的 virtual() 函数,返回适用于当前系统的执行模块字典。
opts (dict) -- Salt options 字典
context (dict) -- 默认在 context 中保存的一个 Salt 上下文
utils (dict) -- Salt 模块应该可以使用的 utils 实用程序功能。 有关配置的其他信息,请参见 salt.config 中的 utils_dirs。
whitelist (list) -- 模块的白名单列表
loaded_base_name (str) -- 加载的基本名称的一个字符串标记。
notify (bool) -- 指示应在模块加载完成时触发事件的标志。
下面来看一段测试代码,首先创建目录 /etc/salt/scripts/,然后创建文件 /etc/salt/scripts/test_minion_mods.py
import salt.config
import salt.loader
__opts__ = salt.config.minion_config('/etc/salt/minion')
__grains__ = salt.loader.grains(__opts__)
__opts__['grains'] = __grains__
__utils__ = salt.loader.utils(__opts__)
__salt__ = salt.loader.minion_mods(__opts__, utils=__utils__)
print(__salt__['cmd.run']('ls /tmp/')) # 调用 cmd.run 执行 ls /tmp/ 命令
执行 python3 /etc/salt/scripts/test_minion_mods.py 查看结果。
加载 raw_mod
功能:返回单个加载的原始模块并绕过 virtual 函数
import salt.config
import salt.loader
__opts__ = salt.config.minion_config('/etc/salt/minion')
testmod = salt.loader.raw_mod(__opts__, 'test', None)
ret = testmod['test.ping']()
print(ret)
states 模块
功能:返回 state 模块
参数:
opts (dict) -- Salt options 字典
functions (dict) -- minions 模块的字典,模块名称为 key,函数为 value。
import salt.config
import salt.loader
__opts__ = salt.config.minion_config('/etc/salt/minion')
statemods = salt.loader.states(__opts__, None, None, None)
grains
salt.loader.grains(opts, force_refresh=False, proxy=None)
功能:返回动态 grains 的函数以及静态 grains 的值
由于 grains 是在启动过程的早期进行计算的,因此 grains 函数没有 salt 或 proxy 可用。 在 proxy-minion 启动时,此函数与 proxy module LazyLoader 对象一起调用,因此 grains 函数可以与其受控设备进行通信。
import salt.config
import salt.loader
__opts__ = salt.config.minion_config('/etc/salt/minion')
__grains__ = salt.loader.grains(__opts__)
print(__grains__['id'])
grain_funcs
salt.loader.grain_funcs(opts, proxy=None)
功能:返回 grain 函数
import salt.config
import salt.loader
__opts__ = salt.config.minion_config('/etc/salt/minion')
grainfuncs = salt.loader.grain_funcs(__opts__)
Salt Client 接口
salt.client.LocalClient
功能:Salt Master 上的 Salt CLI 工具使用的 API 接口
LocalClient 用于向 Salt minions 发送命令以执行执行模块,并将结果返回给 Salt Master。
导入和使用 LocalClient 必须与 Salt Master 在同一台计算机上完成,并且必须使用与 Salt Master 在同一用户上进行。(除非配置了 external_auth,并且执行中包括身份验证凭据)。
import salt.client
local = salt.client.LocalClient()
local.cmd('*', 'test.fib', [10])
# 返回结果 {'www.sublimeatext.com': [55, 1.6689300537109375e-06]}
cmd
功能:以同步的方式在目标 minions 上执行一个管理命令
cmd 方法将执行并等待所有 minions 答复的超时时间,然后它将立即返回所有 minions 的数据。
import salt.client
local = salt.client.LocalClient()
# 输入参数(目标主机,模块,模块参数)
local.cmd('*', 'cmd.run', ['whoami'])
# {'www.sublimeatext.com': 'root'}
为 salt 任务传递更多参数
import salt.client
local = salt.client.LocalClient()
local.cmd('*', 'test.arg', ['arg1', 'arg2'], kwarg={'foo': 'bar'})
多任务发布
import salt.client
local = salt.client.LocalClient()
local.cmd('*', [
'grains.items',
'sys.doc',
'cmd.run',
],
[
[],
[],
['uptime'],
])
参数
tgt (string or list) -- 要执行的目标是哪些 minions。 默认值为 shell glob。 可由 tgt_type 选项修改。
fun (string or list of strings) -- 在指定 minions 上调用的格式为 module.function 的模块与函数。 例如 test.ping 或 grains.items。
复合命令
通过传递一个命令列表,可以在单个发布任务中调用多个功能函数。 这样可以大大降低开销,并加快应用程序与 Salt 的通信。
这就要求 arg 参数是一个 lists 列表。 Fun 列表和 arg 列表必须按索引进行关联,这意味着不带参数的函数在预期索引处仍必须具有相应的空列表。
arg (list or list-of-lists) -- 传递给远程功能函数的参数列表。 如果函数不带参数,则除了执行复合命令时,可以省略 arg。
timeout -- 等待 minions 返回结果的超时时间的秒数。
tgt_type -- tgt 类型的取值范围是:
glob - Bash glob - 此为默认值
list - Python 格式的主机列表
grain - 基于 grain 数值进行匹配
grain_pcre - Grain + regex 匹配
pillar - 使用 pillar 进行匹配
pillar_pcre - Pillar + regex 匹配
nodegroup - 使用 nodegroup 分组匹配
range - 使用 Range server 进行匹配
compound - 使用复合匹配
ipcidr - 使用 ip 地址或 ip 地址段进行匹配
ret -- 使用哪一种 returner 程序。 传递的值可以是单个 returner,也可以是逗号分隔的 returner 列表,以按顺序在各 minions 上调用
kwarg -- 为函数提供的一个关键字参数列表
full_return -- 默认仅输出作业返回结果,完整返回则还包括了退出代码和其他作业元数据。
kwargs -- 可选的关键字参数。 使用 external_auth 时可以传递身份验证凭据。 例如 local.cmd('', 'test.ping', username='saltapi', password='saltapi', eauth='pam'),或者 local.cmd('', 'test.ping', token='5871821ea51754fdcea8153c1c745433')
返回数据
一个包含了执行结果的字典,由 minion ID 作为 key。 如果是复合命令则将返回以函数名称为 key 的子词典。
cmd_async
向连接上的 minions 发送一个异步执行的命令;返回一个 job ID ,如果执行失败则返回 0 。
import salt.client
local = salt.client.LocalClient()
local.cmd_async('*', 'test.sleep', [300])
# '20131219215921857715'
cmd_batch
一次只对部分的 minions 执行命令,直至处理完全部的 minions。
参数:batch,按指定比例分批执行 返回:一个包含了 minion 返回结果的生成器。
import salt.client
local = salt.client.LocalClient()
returns = local.cmd_batch('*', 'state.highstate', batch='10%')
for ret in returns:
print(ret)
cmd_iter
当 minion 连接上来时,执行管理命令并生成返回数据。
通常,对于未连接的 minions,cmd_iter() 不会产生结果。 如果希望它返回未连接的 minions 的结果,需要在 kwargs 中设置 Expect_minions=True。
返回一个基于单个 minion,依次生成返回数据的生成器。
import salt.client
local = salt.client.LocalClient()
ret = local.cmd_iter('*', 'test.ping')
for i in ret:
print(i)
cmd_iter_no_block
当 minion 连接过来时,为单个 minion 生成返回数据,如果没有有效的结果数据则返回 None。
返回一个生成器,基于单个 minion 生成返回数据,或者当没有返回可用时,返回 None。 这允许在 minions 返回之间注入特定的动作。
import salt.client
local = salt.client.LocalClient()
ret = local.cmd_iter_no_block('*', 'test.ping')
for i in ret:
print(i)
# None
# {'www.sublimeatext.com': {'jid': '20210316094827770306', 'retcode': 0, 'ret': True}}
cmd_subset
在目标系统的随机子集上执行命令。
参数:
sub -- 目标系统子集中的目标数量
cli -- 当设置了这个参数时, 返回一个生成器, 否则返回一个包含了 minions 返回数据的字典。
import salt.client
local = salt.client.LocalClient()
local.cmd_subset('*', 'test.ping', sub=1)
get_cli_returns
启动观察程序,查看指定 JID 的返回数据
返回 JID 的所有信息。
get_event_iter_returns
收集事件系统的返回数据,在达到超时时即中止。
run_job
异步发送命令到连接的 minions。准备作业目录,并将命令发布到任何目标 minions。
返回包含作业 ID 和所有期望返回数据的 minions 的列表。
import salt.client
local = salt.client.LocalClient()
local.run_job('*', 'test.sleep', [300])
# {'jid': '20210316094820131543', 'minions': ['www.sublimetext.com']}
Salt Caller
Caller 与 Salt Minion 上 的 salt-call 命令行工具使用的 API 接口相同。
使用该类时并不需要运行中的 master 或 minion 守护程序。
import salt.client
caller = salt.client.Caller()
caller.cmd('test.ping')
import salt.client
import salt.config
__opts__ = salt.config.minion_config('/etc/salt/minion')
__opts__['file_client'] = 'local'
caller = salt.client.Caller(mopts=__opts__)
print(caller.opts)
netapi
netapi 是 salt 自带的 http 服务接口。
安装依赖
pip3 install "cherrypy<=8.9"
pip3 install "tornado==6.0.3"
pip3 install pyopenssl
生成一个自签的数据证书
自签名证书用于启用 https 协议,如果使用 http 协议,可以忽略证书。
salt-call --local tls.create_self_signed_cert
用户
我们需要创建一个用户用来认证执行 salt 命令。
新建文件 /etc/salt/srv/salt/base/user.sls
# 为 saltapi 创建用户并设置密码,密码为 saltapi
saltapi:
user.present:
- shell: /sbin/nologin/
- password: "$1$zqGskr2K$uovCsABf1m0dXhBnimR5Z."
为 Salt-master 执行该 state 文件
salt-call state.apply user --local
配置文件
创建如下目录和文件:
mkdir /etc/salt/log
touch /etc/salt/log/api_error.log
touch /etc/salt/log/api_access.log
编辑文件 /etc/salt/master.d/api.conf,如果不存在需要自行创建。
rest_cherrypy:
port: 8000
debug: True
ssl_crt: /etc/pki/tls/certs/localhost.crt
ssl_key: /etc/pki/tls/certs/localhost.key
log_error_file: /etc/salt/log/api_error.log
log_access_file: /etc/salt/log/api_access.log
# disable_ssl: true # 如果不使用 https,则需要取消注释这个配置
external_auth:
pam:
saltapi:
- .*
- "@wheel"
- "@runner"
该配置文件添加了 salt-api 相关参数,调用 rest_cherrypy ,开启 8000 端口,使用 https 协议,以及认证用户权限。
重启 salt-master
# kill salt-master 进程
pkill salt-master
# 启动 salt-master
salt-master -d -l info
启动 salt-api
salt-api -d -l info
通过 http 进行登录获取 token 认证
curl -X POST -k https://127.0.0.1:8000/login -d username='saltapi' -d password='saltapi' -d eauth='pam' | python -mjson.tool
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 240 100 197 100 43 1102 240 --:--:-- --:--:-- --:--:-- 1106
{
"return": [
{
"eauth": "pam",
"expire": 1609790713.7268956,
"perms": [
".*",
"@wheel",
"@runner"
],
"start": 1609747513.7268941,
"token": "743baaa390d26bca563ed14f164cc94903285da4",
"user": "saltapi"
}
]
}
curl -sSk https://localhost:8000/login -H 'Accept: application/x-yaml' -d username=saltapi -d password=saltapi -d eauth=pam
return:
- eauth: pam
expire: 1576616310.345837
perms:
- .*
- '@wheel'
- '@runner'
start: 1576573110.345836
token: a24562aea31e73de691005e788d03a07d4120161
user: saltapi
通过 Cookie 发送
# 写入 cookie 文件
curl -sSk https://localhost:8000/login -c ~/cookies.txt -H 'Accept: application/x-yaml' -d username=saltapi -d password=saltapi -d eauth=pam
# 读取 cookie 文件,对 ’*‘ 目标主机发送 ping 命令
curl -sSk https://localhost:8000 -b ~/cookies.txt -H 'Accept: application/x-yaml' -d client=local -d tgt='*' -d fun=test.ping
python 程序中使用 session
通过使用 session,在进行登录认证后,session 会保留登录状态,之后就可以利用 session 发送相关操作指令。
通过 ipython 进入环境进行调试。
>>> import requests
>>> session = requests.Session()
# 登录认证
>>> session.post('https://localhost:8000/login', json={
'username': 'saltapi',
'password': 'saltapi',
'eauth': 'pam',
}, verify=False)
# 发送一个测试操作
>>> resp = session.post('https://localhost:8000', json=[{
'client': 'local',
'tgt': '*',
'fun': 'test.arg',
'arg': ['foo', 'bar'],
'kwarg': {'baz': 'Baz!'},
}],verify=False)
# 查看返回数据
>>> resp.json()
{u'return': [{u'www.sublimetext.com': {u'args': [u'foo', u'bar'],
u'kwargs': {u'__pub_arg': [u'foo', u'bar', {u'baz': u'Baz!'}],
u'__pub_fun': u'test.arg',
u'__pub_jid': u'20201102072328701448',
u'__pub_ret': u'',
u'__pub_tgt': u'*',
u'__pub_tgt_type': u'glob',
u'__pub_user': u'saltapi',
u'baz': u'Baz!'}}}]}
api 接口其他功能
查看所有任务
curl -sSk -b ~/cookies.txt -H 'Accept: application/x-yaml' https://localhost:8000/jobs
查看所有 minion key
curl -sSk -b ~/cookies.txt -H 'Accept: application/x-yaml' https://localhost:8000/keys
接口统计数据
curl -sSk -b ~/cookies.txt -H 'Accept: application/x-yaml' https://localhost:8000/stats
Event api
Api 调用 Event
Salt 的 Event 总线可以借助于 salt.netapi.rest_cherrypy.app.Events 实现的 API 服务作为一个 HTTP 流来供外部工具或服务所订阅。
# 替换为我们获取到的 token 字符串
curl -SsNk https://127.0.0.1:8000/events?token=
Python 程序捕获 Event
Python 脚本只能以运行 Salt 的系统用户身份访问事件总线。
可通过 event library( 事件库 )访问事件系统,并且只能由运行 Salt 的同一系统用户访问。 要监听事件,需要创建一个 SaltEvent 对象,然后需要运行 get_event 函数。 SaltEvent 对象需要知道 Salt Unix 套接字的保存位置。 在配置中,这是 sock_dir 选项定义的。 在大多数系统上,sock_dir 选项默认为 /var/run/salt/master。
文件路径 /etc/salt/scripts/salt-event2.py
import salt.config
import salt.utils.event
opts = salt.config.client_config('/etc/salt/master')
while True:
event = salt.utils.event.get_event(
'master',
sock_dir=opts['sock_dir'],
transport=opts['transport'],
opts=opts)
data = event.get_event()
print('\33[33m%s\33[0m' % data)
执行脚本 python3 /etc/salt/scripts/salt-event2.py,然后打开另一个终端,在另一个终端里执行命令 salt-call event.fire '{"data": "message to be sent in the event"}' 'tag' 发送一个事件。
如果某个进程正在侦听该 minion 相关的事件,则对于向 master 主机上的用户触发事件可能很有用。 在非 Windows 系统上的 minions 上监听本地事件的示例:
文件 /etc/salt/scripts/salt-min-event.py
# Minion 服务器监听事件
import salt.utils.event
opts = salt.config.minion_config('/etc/salt/minion')
event = salt.utils.event.MinionEvent(opts)
for evdata in event.iter_events():
# do your processing here...
print(evdata)
自定义 Salt 模块触发 Event 事件
在编写执行模块时,事件可以非常有用,可以用在发生特定任务时通知 master 服务器上的各种进程。 使用常规的交叉调用语法可以轻松完成此操作,操作模块 salt['event.send']。
向文件 /etc/salt/srv/salt/base/_modules/custom.py 添加如下代码:
# /etc/salt/srv/salt/base/_modules/custom.py
# 任务结束后主动发送 Event 事件到 Master
def finish_send_event():
"""
自定义 salt 模块,调用结束后发生 Event
"""
# 发送 Event 事件到 Master
__salt__['event.send']('/salt/custom/module/自定义模块', {
'finished': True,
'message': "The something is finished!",
})
return '这是一个 Salt 发生 Event 事件测试模块'
同步自定义模块 salt '' saltutil.sync_all,然后执行测试 salt '' custom.finish_send_event:
python 脚本触发 Event
文件 /etc/salt/scripts/send-event.py
import salt.client
caller = salt.client.Caller()
# 发送 event 到 master
ret = caller.cmd(
'event.send',
'myco/event/success' ,
{ 'success': True, 'message': "Python 脚本发送 Event 到 Master" }
)
if not ret:
# 发送失败时的操作。
...
ret = caller.cmd(
'event.fire',
{ 'success': True, 'message': "Python 脚本发送 Event 到 Minion" },
'tag'
)
if not ret:
# 发送失败时的操作。
...
分别在不同终端执行如下命令监听事件发生,注意都需要切换到 root 用户。
# 监听 minion 事件
python3 /etc/salt/scripts/salt-min-event.py
# 监听 master 事件
python3 /etc/salt/scripts/salt-event2.py
# 执行脚本发送事件
python3 /etc/salt/scripts/send-event.py
效果如下: