41

SaltStack RESTful API-熊铭浩架构师之路

 4 years ago
source link: https://blog.51cto.com/12643266/2423074
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

SaltStack RESTful API

SaltStack简介
SaltStack作为开源的自动化批量管理工具,功能很强大,在生产环境中也有很多的企业/公司使用,那么如果每次执行都在SaltStack Master上去通过Salt命令执行sls文件或者Salt的其它命令就显得很麻烦,那么我们可以使用Salt提供的API,那么它的API分为local_client和REST API 两种

两种api的区别

  • local_client
    • 调用该api必须要在salt master上运行
    • 它是salt的python模块,即salt自带的python api
    • 依赖于python
  • RESTful API
    • 调用该api的机器可以是任意的计算机系统
    • 基于HTTPS的请求,即任何语言,只需要按照该api的标准,get或者post数据就可以执行salt的相应的操作
    • 官方支持三种RESTful API, 分别是rest_cherry; rest_tonado和rest_wsgi
      • rest_cherry和rest_tonado两个模块支持监听所有的IP的指定端口接收请求
      • rest_wsgi只支持本机访问,只绑定了127.0.0.1

再次声明为何使用RESTful API?
local_client必须依赖于python去调用,必须还得把该python脚本放到salt master本地执行,但是RESTful API支持任意语言调用,因为它是基于https,七层协议

基于rest_cherry RESTful_API官网

注意:本文选择使用rest_cherry模块来实现SaltStack的HTTP API

1、Salt_Master 安装和设置salt-api

1.安装salt-api,并设置开机启动

[root@linux-node1 ~]# yum -y install salt-api pyOpenSSL 
[root@linux-node1 ~]# systemctl enable salt-api

2.配置自签名证书

[root@linux-node1 ~]# cd /etc/pki/tls/certs/
[root@linux-node1 certs]# make testcert
umask 77 ; \
/usr/bin/openssl genrsa -aes128 2048 > /etc/pki/tls/private/localhost.key
Generating RSA private key, 2048 bit long modulus
.......+++
................................+++
e is 65537 (0x10001)
Enter pass phrase:      # 输入加密密码,这里我使用123456
Verifying - Enter pass phrase:  # 确认加密密码
umask 77 ; \
/usr/bin/openssl req -utf8 -new -key /etc/pki/tls/private/localhost.key -x509 -days 365 -out /etc/pki/tls/certs/localhost.crt
Enter pass phrase for /etc/pki/tls/private/localhost.key:
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:CN
State or Province Name (full name) []:Guangdong
Locality Name (eg, city) [Default City]:guangzhou
Organization Name (eg, company) [Default Company Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (eg, your name or your server's hostname) []:
Email Address []:

// 解密key文件,生成无密码的key文件, 过程中需要输入key密码,该密码为之前生成证书时设置的密码
[root@linux-node1 ~]# cd /etc/pki/tls/private/
[root@linux-node1 private]# openssl rsa -in localhost.key -out localhost_nopass.key

2.创建普通用户

[root@linux-node1 ~]# useradd saltapi -M -s /sbin/nologin

// 为新建的saltapi用户设置密码
[root@linux-node1 ~]# echo "salt123456" |passwd --stdin saltapi

3.修改/etc/salt/master文件

[root@linux-node1 ~]# sed -i '/#default_include/s/#default/default/g' /etc/salt/master

4.创建/etc/salt/master.d/目录

[root@linux-node1 ~]# mkdir -p /etc/salt/master.d/
[root@linux-node1 ~]# cd /etc/salt/master.d/
[root@linux-node1 ~]# touch eauth.conf  && touch api.conf

5.编辑eauth.conf,添加下面内容

external_auth:
  pam:           # 可插入式验证模块
    saltapi:     # 用户
      - .*          # 该配置文件给予saltapi用户所有模块使用权限,出于安全考虑一般只给予特定模块使用权限
      - '@wheel'  # 查看salt-key权限
      - '@runner' # 查看minion是否存活权限

6.编辑api.conf,添加下面内容

rest_cherrypy:
  port: 8001
  ssl_crt: /etc/pki/tls/certs/localhost.crt
  ssl_key: /etc/pki/tls/private/localhost_nopass.key

7.启动salt-api

# systemctl restart salt-master
# systemctl start salt-api
# ps -ef|grep salt-api
# netstat -lnput|grep 8001

8.请求salt-api的token

curl -k https://10.0.0.170:8001/login \
-H 'Accept: application/x-yaml' \
-d username=saltapi \
-d password=salt123456 \
-d eauth=pam

return:
- eauth: pam
  expire: 1556680076.98615
  perms:
  - .*
  - '@wheel'
  - '@runner'
  start: 1556636876.986149
  token: 8b3d2a0d9b7708a599173ecd072834321c9d4187
  user: saltapi

2、基于python调用REST API

import requests
import json

# 使用urllib2请求https出错,做的设置
import ssl
context = ssl._create_unverified_context()

# 移除https警告
requests.packages.urllib3.disable_warnings()

salt_api = "https://10.0.0.170:8001/"

class SaltApi:
    """
    定义salt api接口的类
    初始化获得token
    """
    def __init__(self, url):
        self.url = url
        self.username = "saltapi"
        self.password = "salt123456"
        self.headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36",
            "Content-type": "application/json"
        }
        self.params = {'client': 'local', 'fun': '', 'tgt': ''}
        self.login_url = salt_api + "login" # https://10.0.0.170:8001/login
        # 将salt api的用户名和密码,认证方式封装到get参数里
        self.login_params = {'username': self.username, 'password': self.password, 'eauth': 'pam'}
        # 将salt url以及封装的参数,传给get_data函数执行,并获取token
        self.token = self.get_data(self.login_url, self.login_params).get('token')
        # 将token值添加到headers里的X-Auth-Token字段(每次对象执行的时候都会先获取到token,将其赋值给 self.headers['X-Auth-Token'] )
        self.headers['X-Auth-Token'] = self.token

    def get_data(self, url, params):
        '''
        执行post或get请求,并返回执行结果
        :param url: api url地址
        :param params: post传入的参数
        :return:
        '''
        # 必须将 params 携带的参数转换成json格式,才能获得到json格式的返回结果(这是salt的规定)
        if params: # params 不为空执行post方法
            send_data = json.dumps(params)
            request = requests.post(url, data=send_data, headers=self.headers, verify=False)
            response = request.json()
            result = dict(response)
            return result['return'][0]  # 返回结果
        elif params is None: # params 为空执行get方法
            request = requests.get(url, headers=self.headers, verify=False)
            response = request.json()
            result = dict(response)
            return result['return'][0]  # 返回结果

    def get_jobs(self,jid):
        '''
        get方法去获取对应jid的任务执行结果
        :param jid: 接收任务ID/jobs ID
        :return:
        '''
        jobs_url = self.url + "jobs/" +jid
        result = self.get_data(jobs_url,params=None)
        return result

    def salt_command(self, tgt, method, arg=None):
        """
        直接执行salt命令,
        远程执行命令,相当于salt 'client1' cmd.run 'free -m',
        :param tgt: 目标主机 例如(linux-node1 / linux-* / *)
        :param method: 执行的模块 例如(test.ping / cmd.run)
        :param arg:  执行的模块参数  例如(cmd.run -m "df -h")
        :return:
        """
        if arg:
            params = {'client': 'local', 'fun': method, 'tgt': tgt, 'arg': arg}
        else:
            params = {'client': 'local', 'fun': method, 'tgt': tgt}
        result = self.get_data(self.url, params)
        return result

    def salt_sls(self,tgt,mods,pillar=None,saltenv='base'):
        """
        执行salt的sls文件,支持pillar变量和纯sls文件
        :param tgt:    目标主机
        :param mods:    sls文件名,(test.sls == > test),去掉结尾的sls
        :param pillar:  传给文件的pillar变量值
        :param saltenv: sls的环境变量,这里默认为base,如果你有dev、test等,就可以自己传入
        :return:
        """
        if pillar:  # 如果pillar传了参数,则执行下面的代码
            data = {
                'mods': mods,
                'saltenv': saltenv,
                'pillar': pillar,
                "concurrent": True
            }
        elif pillar is None:  # 如果pillar 为None,则执行下面代码
            data = {
                'mods': mods,
                'saltenv': saltenv,
                "concurrent": True
            }

        params = {'client': 'local', 'fun': 'state.sls', 'tgt':tgt,'kwarg':data}
        result = self.get_data(self.url, params)
        return result

    def salt_async_command(self, tgt, method, arg=None):
        """
        异步执行salt命令,只会返回任务id 即jid
        """
        if arg:
            params = {'client': 'local_async', 'fun': method, 'tgt': tgt, 'arg': arg}
        else:
            params = {'client': 'local_async', 'fun': method, 'tgt': tgt}
        # 将返回的jid传给 self.get_data 函数执行并获取该jid的执行结果
        jid_result = self.get_data(self.url, params)
        jid = jid_result['jid']
        result = self.get_jobs(jid)
        return result

    def salt_async_sls(self,tgt,mods,pillar=None,saltenv='base'):
        """
        异步执行salt的sls文件,返回jib
        """
        if pillar:  # 如果pillar传了参数,则执行下面的代码
            data = {
                'mods': mods,
                'saltenv': saltenv,
                'pillar': pillar,
                "concurrent": True
            }
        elif pillar is None:  # 如果pillar 为None,则执行下面代码
            data = {
                'mods': mods,
                'saltenv': saltenv,
                "concurrent": True
            }
        params = {'client': 'local_async', 'fun': 'state.sls', 'tgt':tgt,'kwarg':data}
        # 将返回的jid传给 self.get_data 函数执行并获取该jid的执行结果
        jid_result = self.get_data(self.url, params)
        jid = jid_result['jid']
        result = self.get_jobs(jid)
        return result

    def get_minion_status(self):
        """
        判断当前sallt管理的minion是否存活,并获取返回结果
        :return:
        """
        method = "manage.status"
        params = {'client': 'runner','fun': method,}
        result = self.get_data(self.url, params)
        return result

    def get_grains(self,tgt, arg=None):
        """
        执行grains
        """
        if arg: # 如果arg传入参数,执行下面代码,获取到对应的grains结果
            method = "grains.get"
            params = {'client': 'local', 'fun': method, 'tgt': tgt, 'arg': arg}

        elif arg is None:   # 如果arg为None,执行下面代码,获取到grains的所有结果
            method = "grains.items"
            params = {'client': 'local', 'fun': method, 'tgt': tgt}

        result = self.get_data(self.url, params)
        return result

    def salt_evens(self):
        """
        salt事件监听,
        :return:
        """
        events_url = self.url + "/events"
        result = self.get_data(events_url,params=None)
        print(self.url)
        return result

def salt_main():
    try:
        salt = SaltApi(salt_api)
        return salt
    except Exception as e:
        raise print('saltstack api连接异常')

# 实例化
salt = salt_main()

# 执行sls命令
def x1():
    result = salt.salt_command('*','cmd.run','df -h')
    print(result)

# 检测已添加的minion是否存活
def x2():
    result = salt.get_minion_status()
    print(result)

# 测试salt执行sls
def x3():
    pillar = {'name':'ok'}
    result = salt.salt_sls('*', 't2',pillar)
    # result = salt.salt_sls('*','t2')
    print(result)

# 执行grains
def x4():
    # result = salt.get_grains('*','ip_interfaces')  # 获取所有主机指定的grains信息
    result = salt.get_grains('*')       # 获取所有主机的grains信息
    print(result)

# 异步 执行salt命令
def x5():
    result = salt.salt_async_command('*', 'cmd.run', 'df -h')
    print(result)

# 异步 执行salt sls
def x6():
    pillar = {'name':'ok'}
    result = salt.salt_async_sls('*', 't2',pillar)
    # result = salt.salt_sls('*','t2')
    print(result)

# 执行salt事件监听
def x7():
    result = salt.salt_evens()
    print(result)

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK