python3和python2安装使用salt-api详解

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python3和python2安装使用salt-api详解相关的知识,希望对你有一定的参考价值。

序言

  最近在使用salt-api做主机批量管理部署,整理一下文档。之前使用saltstack 多用于命令行管理,自己做web版的自动化管理平台时,发现命令行的些许局限性,接触到salt-api,找到了替代方式。本文使用的saltstack 版本是2017.7.2最新版本,这个版本中官方加入了python3的支持,之前在使用salt时都是用的Python2版本,现在使用的架构是python3版本的,所在在此探讨下python3使用saltstack以及salt-api的一些方式方法。

 

系统环境:

  CentOS7 + python2/python3 + saltstack2017.7.2

saltstack 安装:

# 更新yum
yum update

# 安装epol源, 安装的版本是2017版本的salt,添加了对于python3的支持
yum install https://repo.saltstack.com/yum/redhat/salt-repo-latest-2.el7.noarch.rpm2.el7.noarch.rpm

# 安装必要软件(mariadb是mysql,用于存储salt命令执行结果和jobid,可不安装)
yum -y install mariadb mariadb-devel mariadb-server wget  python-devel gcc c++ make openssl openssl-devel passwd libffi libffi-devel nginx


# 安装 saltstack
yum -y install salt-master salt-minion salt-api

  

salt-api 安装:

# 上一步已经安装了,写下单独安装的命令
# yum install salt-api  -y

# 创建证书
[[email protected] ~]# cd /etc/pki/tls/certs/
# 生成自签名证书,用于ssl
[[email protected] 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:       # 输入加密密语,4到8191个字符
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 -set_serial 0
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) []:Shanghai  # 选填,可不填写直接回车
Locality Name (eg, city) [Default City]:Shanghai  # 选填,可不填写直接回车
Organization Name (eg, company) [Default Company Ltd]: # 选填,可不填写直接回车
Organizational Unit Name (eg, section) []: # 选填,可不填写直接回车
Common Name (eg, your name or your server‘s hostname) []: # 选填,可不填写直接回车
Email Address []: # 选填,可不填写直接回车
[[email protected] certs]# cd ../private/
# 解密key文件,生成无密码的key文件, 过程中需要输入key密码,该密码为之前生成证书时设置的密码
[[email protected] private]# openssl rsa -in localhost.key -out localhost_nopass.key
Enter pass phrase for localhost.key:
writing RSA key
[[email protected] private]# ls
localhost.key  localhost_nopass.key
# 备注
	如果make testcert出现错误,则删除/etc/pki/tls/private/localhost.key文件,然后再make testcert
# 创建用户(用于salt-api认证)
useradd -M -s /sbin/nologin saltapi && echo "password"|/usr/bin/passwd saltapi --stdin

 

# 升级下pip
    pip install --upgrade pip

# pip 安装一些项目所需环境,这个是我自己的项目所需要的, 可以根据自己需求安装
    pip install Django==1.9
    pip install MySQL-python
    pip install xlwt
    pip install pyOpenSSL   # 这个是你必须安装的

# 备注下,单独安装pip的方式
	wget https://bootstrap.pypa.io/get-pip.py
	python get-pip.py

  

salt-api 配置文件编写:

# 添加配置文件,可以把eauth.conf和api.conf合二为一为api.conf
[[email protected] ~]# mkdir -p /etc/salt/master.d/        
# 这个目录默认不存在,需要手动创建,在/etc/salt/master主配置文件中有指定,类似include
[[email protected] ~]# vim /etc/salt/master.d/eauth.conf   
# 处于安全因素,一般只给特定模块的使用权限,这里给saltapi用户所有模块的使用权限       
external_auth:
  pam:
    saltapi:
      - .*
      - ‘@wheel‘
      - ‘@runner‘
       
[[email protected] ~]# vim /etc/salt/master.d/api.conf 
rest_cherrypy:
  port: 8000                       #  salt-api 监听端口
  ssl_crt: /etc/pki/tls/certs/localhost.crt          # ssl认证的证书
  ssl_key: /etc/pki/tls/private/localhost_nopass.key

# 备注:
    注意所有的缩进都是两个空格,要注意‘:‘后面都有一个空格

  

# salt-api 配置文件详解
port : 必须填写,salt-api启动的端口
host :默认启动于0.0.0.0,可以不填写
debug : 默认为False,True开启后,会输出debug日志
log_access_file : HTTP访问日志的路径,在2016.11.0版本添加的
log_error_file : HTTP错误日志路径,在2016.11.0版本添加的
ssl_crt : SSL证书的绝对路径
ssl_key: SSK证书的私钥绝对路径
ssl_chain : 在使用PyOpenSSL时可选参数,将证书出递给‘ Context.load_verify_locations ‘
disable_ssl : 禁用SSL标识。认证证书将会被送进clear
webhook_disable_auth : False
webhook_url : /hook
thread_pool : 100
socket_queue_size : 30
expire_responses : True
max_request_body_size : 1048576
collect_stats : False
stats_disable_auth : False
更多详细参数请见:https://github.com/saltstack/salt/blob/develop/salt/netapi/rest_cherrypy/app.py
# 启动
systemctl start salt-master
systemctl start salt-minion
systemctl start salt-api

  

Salt-Api使用

Salt-Api 的使用,启动master, minion , api后,测试通过https操作saltstack

# salt-api 使用
# 登陆认证获取token
[[email protected] ~]#  curl -sSk https://localhost:8000/login -H ‘Accept: application/x-yaml‘ -d username=saltapi -d password=password -d eauth=pam
return:
- eauth: pam
  expire: 1511805994.166656
  perms:
  - .*
  - ‘@wheel‘
  - ‘@runner‘
  start: 1511762794.166655
  token: 1bc26f7a595eb08c70780352c5724180d5062876  # 关键
  user: saltapi
# 使用获取的token进行命令操作
[[email protected] ~]# curl -sSk https://localhost:8000 -H ‘Accept: application/x-yaml‘ -H ‘X-Auth-Token: 12ff12468f7ae98d4880fd9a627bf8ef87942d5a‘ -d client=local  -d tgt=‘*‘ -d fun=test.ping
return:
- minion: true

参数解释:
client : 模块,python处理salt-api的主要模块,‘client interfaces <netapi-clients>’
	local : 使用‘LocalClient <salt.client.LocalClient>’ 发送命令给受控主机,等价于saltstack命令行中的‘salt‘命令
	local_async : 和local不同之处在于,这个模块是用于异步操作的,即在master端执行命令后返回的是一个jobid,任务放在后台运行,通过产看jobid的结果来获取命令的执行结果。
	runner : 使用‘RunnerClient<salt.runner.RunnerClient>‘ 调用salt-master上的runner模块,等价于saltstack命令行中的‘salt-run‘命令
	runner_async : 异步执行runner模块
	wheel : 使用‘WheelClient<salt.wheel.WheelClient>‘, 调用salt-master上的wheel模块,wheel模块没有在命令行端等价的模块,但它通常管理主机资源,比如文件状态,pillar文件,salt配置文件,以及关键模块<salt.wheel.key>功能类似于命令行中的salt-key。
	wheel_async : 异步执行wheel模块
	备注:一般情况下local模块,需要tgt和arg(数组),kwarg(字典),因为这些值将被发送到minions并用于执行所请求的函数。而runner和wheel都是直接应用于master,不需要这些参数。
tgt : minions
fun : 函数
arg : 参数
expr_form : tgt的匹配规则
	‘glob‘ - Bash glob completion - Default
	‘pcre‘ - Perl style regular expression
	‘list‘ - Python list of hosts
	‘grain‘ - Match based on a grain comparison
	‘grain_pcre‘ - Grain comparison with a regex
	‘pillar‘ - Pillar data comparison
	‘nodegroup‘ - Match on nodegroup
	‘range‘ - Use a Range server for matching
	‘compound‘ - Pass a compound match string

    

Python3使用saltstack和salt-api

  从开始到现在,所有的操作都是在python2环境下安装完成的,如果要使用python3呢?

# 首先安装python3

    1. tar zxvf Python-3.5.1.tgz   
    2. cd  Python-3.5.1        
    3. ./configure
    4. make
    5. make install
    6. mv  /usr/bin/python /usr/bin/python2 # 如果是软连接,可以直接删除
    7. ln -s /usr/local/bin/python3.5 /usr/bin/python
    8. vim /usr/bin/yum   # 修改Yum,使yum依然有效,yum依靠老版本的python
    9. #!/usr/bin/python 修改为#!/usr/bin/python2
        
# 修改完/usr/bin/yum 依然还有问题,可以尝试修改/usr/libexec/urlgrabber-ext-down的文件python抬头

# 使用Python3直接启动salt,因为默认环境已经切换的python3, 所以直接启动即可
systemctl start salt-master
systemctl start salt-minion
systemctl start salt-api


# 之前有朋友遇到了切换到python3后产生了很多问题,可以试着执行下
python3 -m pip install salt==2017.7.2

 

 

Class SaltApi

  现在所有的操作还是基于命令行模式,在项目中不能这么使用,我们可以写一个基于salt-api的类,方便项目代码的调用。在这里特别附上python2、python3两个版本的salt-api class, 在使用中发现,python3版本的salt-api class 是可以直接去请求管理python2版本下的saltstack,这样就解决了一些跨python版本的问题,毕竟现在主流操作系统默认安装的还是python2,避免了手动升级python3, 可以让saltstack在python2下继续运行,而我们可以通过python3去管理saltstack。

技术分享图片
  1 #coding:utf8
  2 import urllib, json
  3 import urllib.request
  4 import urllib.parse
  5 import ssl
  6 ssl._create_default_https_context = ssl._create_unverified_context
  7 
  8 
  9 class SaltAPI(object):
 10     __token_id = ‘‘
 11 
 12     def __init__(self,url,username,password):
 13         self.__url = url.rstrip(/)
 14         self.__user = username
 15         self.__password = password
 16 
 17     def token_id(self):
 18         """
 19             用户登陆和获取token
 20         :return:
 21         """
 22         params = {eauth: pam, username: self.__user, password: self.__password}
 23         encode = urllib.parse.urlencode(params)
 24         obj = urllib.parse.unquote(encode).encode(utf-8)
 25         content = self.postRequest(obj,prefix=/login)
 26         try:
 27             self.__token_id = content[return][0][token]
 28         except KeyError:
 29             raise KeyError
 30 
 31     def postRequest(self,obj,prefix=/):
 32         url = self.__url + prefix
 33         headers = {X-Auth-Token: self.__token_id}
 34         req = urllib.request.Request(url, obj, headers)
 35         opener = urllib.request.urlopen(req)
 36         content = json.loads(opener.read())
 37         return content
 38 
 39     def list_all_key(self):
 40         """
 41             获取包括认证、未认证salt主机
 42         """
 43 
 44         params = {client: wheel, fun: key.list_all}
 45         obj = urllib.parse.urlencode(params).encode(utf-8)
 46         self.token_id()
 47         content = self.postRequest(obj)
 48         minions = content[return][0][data][return][minions]
 49         minions_pre = content[return][0][data][return][minions_pre]
 50         return minions, minions_pre
 51 
 52     def delete_key(self, node_name):
 53         ‘‘‘
 54             拒绝salt主机
 55         ‘‘‘
 56 
 57         params = {client: wheel, fun: key.delete, match: node_name}
 58         obj = urllib.parse.urlencode(params).encode(utf-8)
 59         self.token_id()
 60         content = self.postRequest(obj)
 61         ret = content[return][0][data][success]
 62         return ret
 63 
 64     def accept_key(self,node_name):
 65         ‘‘‘
 66             接受salt主机
 67         ‘‘‘
 68 
 69         params = {client: wheel, fun: key.accept, match: node_name}
 70         obj = urllib.parse.urlencode(params).encode(utf-8)
 71         self.token_id()
 72         content = self.postRequest(obj)
 73         ret = content[return][0][data][success]
 74         return ret
 75 
 76     def salt_get_jid_ret(self,jid):
 77         ‘‘‘
 78             通过jid获取执行结果
 79         ‘‘‘
 80 
 81         params = {client:runner, fun:jobs.lookup_jid, jid: jid}
 82         obj = urllib.parse.urlencode(params).encode(utf-8)
 83         self.token_id()
 84         content = self.postRequest(obj)
 85         ret = content[return][0]
 86         return ret
 87 
 88     def salt_running_jobs(self):
 89         ‘‘‘
 90             获取运行中的任务
 91         ‘‘‘
 92 
 93         params = {client:runner, fun:jobs.active}
 94         obj = urllib.parse.urlencode(params).encode(utf-8)
 95         self.token_id()
 96         content = self.postRequest(obj)
 97         ret = content[return][0]
 98         return ret
 99 
100     def remote_noarg_execution(self, tgt, fun):
101         """
102             执行命令没有参数
103         :param tgt: 目标主机
104         :param fun:  执行模块
105         :return:
106         """
107         params = {client: local, tgt:tgt, fun: fun}
108         obj = urllib.parse.urlencode(params).encode(utf-8)
109         self.token_id()
110         content = self.postRequest(obj)
111         ret = content[return][0].values()
112         return ret
113 
114     def remote_execution(self,tgt,fun,arg):
115         ‘‘‘ 执行命令有参数 ‘‘‘
116         params = {client: local, tgt: tgt, fun: fun, arg: arg}
117         obj = urllib.parse.urlencode(params).encode(utf-8)
118         self.token_id()
119         content = self.postRequest(obj)
120         print(content)
121         ret = content[return][0][tgt]
122         return ret
123 
124     def remote_execution_module(self,tgt,fun,arg):
125         ‘‘‘
126             异步执行远程命令、部署模块
127         ‘‘‘
128 
129         params = {client: local_async, tgt: tgt, fun: fun, arg: arg, expr_form: list}
130         obj = urllib.parse.urlencode(params).encode(utf-8)
131         self.token_id()
132         content = self.postRequest(obj)
133         print(content)
134         jid = content[return][0][jid]
135         return jid
136 
137     def remote_localexec(self,tgt,fun,arg):
138         params = {client: local, tgt: tgt, fun: fun, arg: arg, expr_form: list}
139         obj = urllib.parse.urlencode(params).encode(utf-8)
140         self.token_id()
141         content = self.postRequest(obj)
142         print(content)
143         ret = content[return][0]
144         return ret
145 
146     def salt_state(self,tgt,arg,expr_form):
147         ‘‘‘
148         sls文件
149         ‘‘‘
150         params = {client: local, tgt: tgt, fun: state.sls, arg: arg, expr_form: expr_form}
151         obj = urllib.parse.urlencode(params).encode(utf-8)
152         self.token_id()
153         content = self.postRequest(obj)
154         ret = content[return][0]
155         return ret
156 
157     def project_manage(self,tgt,fun,arg1,arg2,arg3,arg4,arg5,expr_form):
158         ‘‘‘
159         文件上传、备份到minion、项目管理
160         ‘‘‘
161         params = {client: local, tgt: tgt, fun: fun, arg: arg1, expr_form: expr_form}
162         # 拼接url参数
163         params2 = {arg:arg2}
164         arg_add = urllib.parse.urlencode(params2)
165         obj = urllib.parse.urlencode(params).encode(utf-8)
166         obj = obj + & + arg_add
167         params3 = {arg: arg3}
168         arg_add = urllib.parse.urlencode(params3)
169         obj = obj + & + arg_add
170         params4 = {arg: arg4}
171         arg_add = urllib.parse.urlencode(params4)
172         obj = obj + & + arg_add
173         params5 = {arg: arg5}
174         arg_add = urllib.parse.urlencode(params5)
175         obj = obj + & + arg_add
176         self.token_id()
177         content = self.postRequest(obj)
178         ret = content[return][0]
179         return ret
180 
181     def file_copy(self,tgt,fun,arg1,arg2,expr_form):
182         ‘‘‘
183         文件上传、备份到minion、项目管理
184         ‘‘‘
185         params = {client: local, tgt: tgt, fun: fun, arg: arg1, expr_form: expr_form}
186         # 拼接url参数
187         params2 = {arg:arg2}
188         arg_add = urllib.parse.urlencode(params2)
189         obj = urllib.parse.urlencode(params).encode(utf-8)
190         obj = obj + & + arg_add
191         self.token_id()
192         content = self.postRequest(obj)
193         ret = content[return][0]
194         return ret
195 
196     def file_bak(self,tgt,fun,arg,expr_form):
197         ‘‘‘
198         文件备份到master
199         ‘‘‘
200         params = {client: local, tgt: tgt, fun: fun, arg: arg, expr_form: expr_form}
201         obj = urllib.parse.urlencode(params).encode(utf-8)
202         self.token_id()
203         content = self.postRequest(obj)
204         ret = content[return][0]
205         return ret
206 
207     def file_manage(self,tgt,fun,arg1,arg2,arg3,expr_form):
208         ‘‘‘
209         文件回滚
210         ‘‘‘
211         params = {client: local, tgt: tgt, fun: fun, arg: arg1, expr_form: expr_form}
212         params2 = {arg: arg2}
213         arg_add = urllib.parse.urlencode(params2)
214         obj = urllib.parse.urlencode(params).encode(utf-8)
215         obj = obj + & + arg_add
216         params3 = {arg: arg3}
217         arg_add_2 = urllib.parse.urlencode(params3)
218         obj = obj + & + arg_add_2
219         self.token_id()
220         content = self.postRequest(obj)
221         ret = content[return][0]
222         return ret
223 
224     def salt_alive(self,tgt):
225         ‘‘‘
226         salt主机存活检测
227         ‘‘‘
228 
229         params = {client: local, tgt: tgt, fun: test.ping}
230         obj = urllib.parse.urlencode(params).encode(utf-8)
231         self.token_id()
232         content = self.postRequest(obj)
233         ret = content[return][0]
234         return ret
235 
236     def remote_server_info(self,tgt,fun):
237         ‘‘‘
238             获取远程主机信息
239         ‘‘‘
240         params = {client: local, tgt: tgt, fun: fun}
241         obj = urllib.parse.urlencode(params).encode(utf-8)
242         self.token_id()
243         content = self.postRequest(obj)
244         ret = content[return][0][tgt]
245 
246         return ret
247 
248 
249 if __name__ == __main__:
250         salt = SaltAPI(url=https://localhost:8000,username=saltapi,password=password!)
251         minions, minions_pre = salt.list_all_key()
252         print(minions)
python3版本salt-api class
技术分享图片
# -*- coding: utf-8 -*-

import urllib2,urllib
import time
import ssl
ssl._create_default_https_context = ssl._create_unverified_context
try:
    import json
except ImportError:
    import simplejson as json

class SaltAPI(object):
    __token_id = ‘‘
    def __init__(self,url,username,password):
        self.__url = url.rstrip(/)
        self.__user = username
        self.__password = password

    def token_id(self):
        ‘‘‘ user login and get token id ‘‘‘
        params = {eauth: pam, username: self.__user, password: self.__password}
        encode = urllib.urlencode(params)
        obj = urllib.unquote(encode)
        content = self.postRequest(obj,prefix=/login)
        try:
                self.__token_id = content[return][0][token]
        except KeyError:
                raise KeyError

    def postRequest(self,obj,prefix=/):
        url = self.__url + prefix
        headers = {X-Auth-Token   : self.__token_id}
        req = urllib2.Request(url, obj, headers)
        opener = urllib2.urlopen(req)
        content = json.loads(opener.read())
        return content

    def list_all_key(self):
        ‘‘‘
        获取包括认证、未认证salt主机
        ‘‘‘
        params = {client: wheel, fun: key.list_all}
        obj = urllib.urlencode(params)
        self.token_id()
        content = self.postRequest(obj)
        # 认证的主机列表
        minions = content[return][0][data][return][minions]
        # 未认证的主机列表
        minions_pre = content[return][0][data][return][minions_pre]
        return minions,minions_pre

    def delete_key(self,node_name):
        ‘‘‘
        拒绝salt主机,删除主机
        ‘‘‘
        params = {client: wheel, fun: key.delete, match: node_name}
        obj = urllib.urlencode(params)
        self.token_id()
        content = self.postRequest(obj)
        ret = content[return][0][data][success]
        return ret

    def accept_key(self,node_name):
        ‘‘‘
        接受salt主机
        ‘‘‘
        params = {client: wheel, fun: key.accept, match: node_name}
        obj = urllib.urlencode(params)
        self.token_id()
        content = self.postRequest(obj)
        ret = content[return][0][data][success]
        return ret

    def remote_noarg_execution(self,tgt,fun):
        ‘‘‘ 
        执行命令没有参数
        tgt:目标主机
        fun: 执行模块,例如“test.ping”
        ‘‘‘
        params = {client: local, tgt: tgt, fun: fun}
        obj = urllib.urlencode(params)
        self.token_id()
        content = self.postRequest(obj)
        ret = content[return][0].values()
        # return ret
        return ret

    def remote_execution(self,tgt,fun,arg):
        ‘‘‘ 执行命令有参数 ‘‘‘
        params = {client: local, tgt: tgt, fun: fun, arg: arg}
        obj = urllib.urlencode(params)
        self.token_id()
        content = self.postRequest(obj)
        ret = content[return][0][tgt]
        return ret

    def target_remote_execution(self,tgt,fun,arg):
        ‘‘‘ 异步执行远程命令,部署模块 ‘‘‘
        params = {client: local, tgt: tgt, fun: fun, arg: arg, expr_form: nodegroup}
        obj = urllib.urlencode(params)
        self.token_id()
        content = self.postRequest(obj)
        jid = content[return][0][jid]
        return jid

    def deploy(self,tgt,arg):
        ‘‘‘ 模块部署 ‘‘‘
        params = {client: local, tgt: tgt, fun: state.sls, arg: arg}
        obj = urllib.urlencode(params)
        self.token_id()
        content = self.postRequest(obj)
        return content

    def async_deploy(self,tgt,arg):
        ‘‘‘ 异步执行向客户端发送命令 ‘‘‘
        params = {client: local_async, tgt: tgt, fun: state.sls, arg: arg}
        obj = urllib.urlencode(params)
        self.token_id()
        content = self.postRequest(obj)
        jid = content[return][0][jid]
        return jid

    def target_deploy(self,tgt,arg):
        ‘‘‘ Based on the node group forms deployment ‘‘‘
        params = {client: local_async, tgt: tgt, fun: state.sls, arg: arg, expr_form: nodegroup}
        obj = urllib.urlencode(params)
        self.token_id()
        content = self.postRequest(obj)
        jid = content[return][0][jid]
        return jid

def main():
    sapi = SaltAPI(url=https://localhost:8000,username=saltapi,password=password!)
    sapi.token_id()
    #print sapi.list_all_key()

if __name__ == __main__:
    main()
python2版本salt-api class

 

以上是关于python3和python2安装使用salt-api详解的主要内容,如果未能解决你的问题,请参考以下文章

安装python3.8和python2.7

CentOS7下安装Python3和Python2并存

CentOS7 Python2 和Python3 共存(Python3安装)

在同一台电脑上同时安装Python2和Python3

在同一台电脑上同时安装Python2和Python3

python2和python3并存时库的安装方法