174SaltStack 运维通关手册--SaltStack API 使用

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 注入特殊变量(saltopts等)。
大多数模块可以手动加载。 这在第三方 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 函数没有 saltproxy 可用。 在 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
image.png

配置文件

创建如下目录和文件:

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"
        }
    ]
}
image.png
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

image.png

通过 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

image.png

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
image.png

查看所有 minion key

curl -sSk -b ~/cookies.txt -H 'Accept: application/x-yaml' https://localhost:8000/keys
image.png

接口统计数据

curl -sSk -b ~/cookies.txt -H 'Accept: application/x-yaml' https://localhost:8000/stats

image.png

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=
image.png

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' 发送一个事件。


image.png

image.png

如果某个进程正在侦听该 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:

image.png

image.png

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

效果如下:


image.png

image.png

你可能感兴趣的:(174SaltStack 运维通关手册--SaltStack API 使用)