从零开始搭建Salt Web之封装salt-api接口

salt-api现在已经正常运行,接下来则是实现通过调用salt-api来执行salt命令。

调用salt-api执行命令时,记得携带c_path参数

因为salt中自带了tornado这个库,所以决定基于tornado.httpclient来封装HTTP请求。

交互模式:

>>> import json
>>> from tornado.httpclient import HTTPClient, HTTPRequest
>>> client = HTTPClient()
# 请求头中声明通过json提交内容
>>> headers = {'Content-Type': 'application/json'} 
>>> body1 = {'username': 'salttest', 'password': 'password', 'eauth': 'pam'}
>>> url = 'https://localhost:8090/'
# 这里指定需指定validate_vert=False, 否则HTTPClient无法访问https
>>> request1 = HTTPRequest(url=url+'login', method='POST', headers=headers, body=json.dumps(body), validate_cert=False)
>>> response1 = client.fetch(request1)
>>> response1.body
'{"return": [{"perms": [".*"], "start": 1488443323.968138, 
"token": "0daf377b4611db***8419f515d18744338", 
"expire": 1488486523.968139, "user": "uyun", "eauth": "pam"}]}'
>>> headers['X-Auth-Token'] = '0daf377b4611db***8419f515d18744338'
>>> body2 = {'client': 'local', 'tgt': '*', 'fun': 'test.ping', 'c_path': '/root/SaltWeb/conf'}
>>> request2 = HTTPRequest(url=url, method='POST', headers=headers, body=json.dumps(body), validate_cert=False)
>>> response2 = client.fetch(request2)
>>> response2.body
'{"return": [{"10.1.240.213": "localhost.localdomain"}]}'

以上就是大致流程,接下来对操作进行简单的封装。

# coding: utf-8
import json
from urlparse import urljoin
from tornado.httpclient import HTTPClient, HTTPRequest, HTTPError


class SaltClient(object):
    def __init__(self, url, username, password, c_path=None):
        self._url = url
        self._un = username
        self._pw = password
        self._cpath = c_path

        self._token = None
    
    @property
    def headers(self):
        headers = {'Content-Type': 'application/json',
                   'Accept': 'application/json'}
        if self._token:
            headers['X-Auth-Token'] = self._token
        return headers
    
    def get_token(self):
        url = urljoin(self._url, 'login')
        params = {'username': self._un,
                'password': self._pw,
                'eauth': 'pam'}
        response = self.post(url, params)
        return response['return'][0]['token']
    
    def _request(self, url, method, body, validate_cert=False, **kwargs):
        return HTTPRequest(url=url,
                           method=method,
                           headers=self.headers,
                           body=json.dumps(body),
                           validate_cert=validate_cert,
                           **kwargs)
    
    def post(self, url, params):
        client = HTTPClient()
        try:
            request = self._request(url, 'POST', params)
            response = client.fetch(request).body
        except HTTPError as e:
            if e.code == 401:
                self._token = self.get_token()
                response = self.post(url, params)
            else:
                raise
        if isinstance(response, str):
            response = json.loads(response)
        return response
    
    def cmd(self, client, tgt, fun, arg=None, **kwargs):
        params = {'client': client, 'tgt': tgt, 'fun': fun}
        if arg:
            params['arg'] = arg
        if self._cpath:
            params['c_path'] = self._cpath
        ret = self.post(self._url, params)
        return ret['return']

逻辑很简单,主要通过调用cmd()方法执行命令,因为token存在时效性,当token过期时,
调用命令会抛出401错误授权的异常,捕获到之后重新获取一次token,依次循环。

posted @ 2017-03-02 22:14  agnewee  阅读(1766)  评论(0编辑  收藏  举报