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
[root@linux-node1 ~]# 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)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章