zoukankan      html  css  js  c++  java
  • 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官网](https://docs.saltstack.com/en/latest/ref/netapi/all/salt.netapi.rest_cherrypy.html#authentication)

    注意:本文选择使用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

    3.创建普通用户

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

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

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

    5.创建/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

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

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

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

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

    6.启动salt-api

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

    7.测试请求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)
    
    
    
  • 相关阅读:
    Django ---uploads files
    powershell 更改为Oh-my-zsh
    Ubuntu server 安装Mysql
    Ubuntu下安装Python多版本开发环境
    python virtualenv 虚拟开发环境
    csv文件操作
    Could not load file or assembly ADODB, Version=7.0.3300.0
    sqlserver 循环截取字段中的某些字符
    JSON序列化的长度
    为何HttpContext.Current为NULL
  • 原文地址:https://www.cnblogs.com/jasonminghao/p/12250967.html
Copyright © 2011-2022 走看看