描述:大数据平台存在100+主机,需要给指定用户创建互信,实现集群内ssh免密码登录。
工具:python,paramiko,multiprocessing
原理:利用paramiko模拟ssh登录执行命令并获取返回结果,利用multiprocessing实现多进程,提高执行效率
运行流程:[检测主机存活]->[检测主机是否存在公钥]->[不存在公钥主机执行ssh-keygen命令]->[获取每台主机公钥]->[分发公钥至每台主机]
实现代码:
#!/usr/bin/env python
#-*- coding:utf-8 -*-
#auth:dWX278035
#update:2017/2/22 10:12
#description:Flasky
import os,paramiko,sys,time,ConfigParser,subprocess
from multiprocessing import Process,Pool
import base64
'''
描述:主机互信自动创建
使用方法:python linuxMutualTrust.py test.conf
参数描述:主机列表conf文件
注:
(1)主机不存活不建立互信
(2)主机存在公钥则不会重复生成,自动读取已有公钥
(3)若存在互信,脚本不会清除authorized_keys,会追加公钥至authorized_keys文件
'''
''' 主机存活检测命令'''
pingCheck=['ping','-c','3','-i','0.2','-W','1']
''' ssh-keygen 命令 '''
keygenCmd='''ssh-keygen -q -t rsa -P '' -f ~/.ssh/id_rsa && echo yes || echo no'''
''' 读取pub文件 '''
readPubCmd='''test -f ~/.ssh/id_rsa.pub && cat ~/.ssh/id_rsa.pub || echo '''
''' 查看pub文件是否存在 '''
testExistsCmd='''test -f ~/.ssh/id_rsa.pub && echo yes || echo no'''
''' 追加公钥至authorized_keys中'''
addKeyCmd='''echo %s >> ~/.ssh/authorized_keys && echo yes || echo no'''
''' 消息返回分隔符,应尽量复杂'''
_mess_part='''|+|'''
''' 进程池大小 '''
PROCESSES=30
''' 初始化配置文件 '''
class InitConf:
def __init__(self,conf_file):
try:
file_path = os.path.join(sys.path[0],conf_file)
self.cf = ConfigParser.SafeConfigParser()
self.cf.read(conf_file)
except Exception as e:
Log('RED','ConfigParserError:%s' % (str(e)))
sys.exit(1)
''' 读取文件配置信息 '''
def readConf(self):
Log('GREEN','开始初始化配置信息...')
hosts=[]
try:
opts = eval(self.cf.get('host','connList'))
for opt in opts:
host = []
s = eval(self.cf.get('information',opt))
host.append(s['ip'])
host.append(s['user'])
host.append(base64.b64decode(s['password']))
host.append(s['port'])
hosts.append(host)
except Exception as e:
Log('RED','ReadConfError:%s' % (str(e)))
sys.exit(1)
return hosts
'''检测主机存活 '''
def checkAloneHost(host_info):
ip = host_info[0]
_pingCheck = pingCheck
_pingCheck.append(ip)
try:
child = subprocess.Popen(_pingCheck,stdout=subprocess.PIPE)
_status = child.communicate()
transmitted = _status[0].split('\n')[-3].split(',')[0].split()[0]
received = _status[0].split('\n')[-3].split(',')[1].split()[0]
if int(transmitted) != int(received) :
Log('RED','[%s]:[%s] %s' % (ip,'ping',str(e)))
return '%s%s%s' %(ip,_mess_part,'no')
else:
return '%s%s%s' %(ip,_mess_part,'yes')
except Exception:
return '%s%s%s' %(ip,_mess_part,'no')
''' 多进程检测主机存活状态'''
def checkHostsAlive(server_list):
Log('GREEN','开始检测主机是否存活...')
result,_alive,_died=[],[],[]
p = Pool(processes = PROCESSES)
for h in server_list:
result.append(p.apply_async(checkAloneHost,[h,]))
p.close()
p.join()
for rs in result:
if rs is None:
break
ip,status = rs.get().strip().split(_mess_part)
if status == 'yes':
_alive.append(ip)
else:
_died.append(ip)
return _alive,_died
''' 初始化ssh服务 '''
def init_server():
server = paramiko.SSHClient()
server.load_system_host_keys()
server.set_missing_host_key_policy(paramiko.AutoAddPolicy())
return server
''' 定义日志输出格式 '''
def Log(type,msg):
date_detail = time.strftime('%Y-%m-%d %H:%M:%S')
logText='[%s] %s' %(date_detail,msg)
if type == 'NOMAL':
print '\033[32;1m%s\033[0m' %(msg)
elif type == 'GREEN':
print '\033[32;1m[INFO ] %s\033[0m' %(logText)
elif type == 'RED':
print '\033[31;1m[ERROR] %s\033[0m' %(logText)
elif type == 'YELLOW':
print '\033[33;1m[WARN ] %s\033[0m' %(logText)
''' 命令执行函数 '''
def ssh_run(host_info,cmd,server):
try:
ip,username,password,port= host_info[0],host_info[1],host_info[2],host_info[3]
server.connect(ip,int(port),username,password,timeout=5)
stdin,stdout,stderr = server.exec_command(cmd)
return '%s%s%s' %(ip,_mess_part,stdout.read().replace('\n',' '))
except Exception as e:
Log('RED','[%s]:[%s] %s' % (ip,cmd,str(e)))
return '%s%s%s' %(ip,_mess_part,'error')
''' 执行命令 '''
def exec_pools_cmd(server_list,cargs):
result=[]
p = Pool(processes = PROCESSES)
for h in server_list:
result.append(p.apply_async(ssh_run,[h,] + cargs))
p.close()
p.join()
return result
''' 判断当前用户.ssh目录下公钥是否存在 '''
def checkExistsKey(server_list,server):
_exists,_not_exists,_error=[],[],[]
result = exec_pools_cmd(server_list,[testExistsCmd,server])
for rs in result:
ip,status = rs.get().strip().split(_mess_part)
if status == 'yes':
_exists.append(ip)
elif status == 'no':
_not_exists.append(ip)
else:
_error.append(ip)
return _exists,_not_exists,_error
''' 读取当前用户.pub文件,并返回结果信息'''
def readPubInfo(server_list,server):
Log('GREEN','开始读取主机id_rsa.pub文件...')
_authorized_keys=[]
result = exec_pools_cmd(server_list,[readPubCmd,server])
for rs in result:
ip,keys = rs.get().split(_mess_part)
if keys != "":
_authorized_keys.append(keys)
else:
Log('RED','[%s] %s' %(ip,'Read id_rsa.pub error!'))
return _authorized_keys
''' 登录主机执行ssh-keygen命令 '''
def exec_ssh_keygen(server_list,server):
Log('GREEN','开始执行ssh-keygen...')
_success,_error=[],[]
result = exec_pools_cmd(server_list,[keygenCmd,server])
for rs in result:
ip,status = rs.get().strip().split(_mess_part)
if status == 'yes':
_success.append(ip)
else:
_error.append(ip)
return _success,_error
''' 检查主机是否存在公钥 '''
def checkStatus(server_list,server):
_exists_hosts,_not_exists_hosts,_error_hosts = [],[],[]
print('\n-------------检查结果----------------')
_exists,_not_exists,_error = checkExistsKey(server_list,server)
print('已存在:[%s]个' %(len(_exists)))
if len(_not_exists_hosts) >0 :
print('不存在:[%s]' %(len(_not_exists)))
if len(_error) > 0 :
print('执行命令错误主机:%s' %(_error))
print('-------------------------------------\n')
for se in server_list:
if se[0] in _not_exists:
_not_exists_hosts.append(se)
elif se[0] in _exists:
_exists_hosts.append(se)
else:
_error_hosts.append(se)
return _exists_hosts,_not_exists_hosts,_error_hosts
''' 将公钥写入authorized_keys'''
def writeKeys(server_list,server,keys):
Log('GREEN','开始写入authorized_keys...')
add_cmd = addKeyCmd %(keys)
result = exec_pools_cmd(server_list,[add_cmd,server])
for rs in result:
ip,status = rs.get().strip().split(_mess_part)
if status != 'yes':
Log('RED','[Write keys error] %s %s' %(ip,status))
''' 主函数 '''
def main(conf_name):
p = InitConf(conf_name)
server_list = p.readConf()
server = init_server()
alive_host = []
_alive,_died = checkHostsAlive(server_list)
Log('GREEN','存活主机: %s/%s ' %(len(_alive),len(server_list)))
if len(_died) > 0:
Log('RED','死亡主机列表: %s') %(str(_died))
for _x in server_list:
if _x[0] in _alive:
alive_host.append(_x)
else:
alive_host = server_list
Log('GREEN','执行前ssh-keygen检查...')
chk_ok_hosts,chk_need_hosts,chk_error_hosts=checkStatus(alive_host,server)
if len(chk_need_hosts) > 0:
_exec_success,_exec_error = exec_ssh_keygen(chk_need_hosts,server)
Log('GREEN','执行成功:[%s]个' %(len(_exec_success)))
if len(_exec_error) > 0 :
Log('RED','执行ssh-keygen失败列表:%s' %(_exec_errror))
Log('GREEN','执行后ssh-keygen检查...')
chk_ok_hosts,chk_need_hosts,chk_error_hosts=checkStatus(alive_host,server)
if len(chk_error_hosts) > 0:
Log('RED','互信失败列表:%s' %(chk_error_hosts))
else:
Log('YELLOW','所有主机均存在公钥!!!')
_authorized_keys = readPubInfo(chk_ok_hosts,server)
for keys in _authorized_keys:
writeKeys(chk_ok_hosts,server,keys)
''' 程序入口 '''
if __name__ == '__main__':
if len(sys.argv) < 2:
Log('RED','Usage: python %s test.conf' %(sys.argv[0]))
sys.exit(1)
else:
conf_name = sys.argv[1]
main(conf_name)
注: (1)依赖的conf文件参考前两章中配置文件生成。 (2)代码在小集群中运行通过,可能存在BUG,如有发现请反馈,谢谢。