一 应用场景描述
有这个么一个需求:对很多台服务器的Java程序所占用的端口数量与正常的值进行对比确认。
可以使用如下命令得到结果:
netstat -tulnp|grep java|wc -l
但是服务器很多,每台手动去执行这条命令是不现实的。于是想到使用Ansible批量去执行,Ansible使用paramiko去ssh登录服务器执行命令,并使用mutilprocessing实现多进程ssh登录。
二 代码实现
如果直接使用Ansible去执行,不作任何处理的话。执行情况是这样的:
# ansible -i qa_servers.txt all --private-key=/root/.ssh/id_rsa -m shell -a "netstat -tulnp|grep java|wc -l" 172.30.25.71 | success | rc=0 >> 3 172.30.25.179 | success | rc=0 >> 19 172.30.25.180 | success | rc=0 >> 86 172.30.25.181 | success | rc=0 >> 82
ansible就是Ansible命令执行的命令, -i 单独指定hosts文件 --private-key指定ssh私钥路径 -m指定需要调用的模块,这里调用shell模块
-a 指定给模块传递的参数,这里既是要执行的命令
这里出现了几个问题:
a.每台服务器上的正常Java端口数量不同,并且需要和正常的值进行比对和确认,最好可以打印输出结果
b.在命令行直接调用ansible虽然对每台服务器都执行了命令但是输出结果很不规范,需要进一步处理。
Ansible本来就是Python开发的,安装完Ansible后所有的代码都位于/usr/lib/python2.6/site-packages/ansible/目录下,所以可以当作阅读正常python代码来查看Ansible源代码。
再通过查看Ansible官网的代码示例得知Ansible执行命令都是通过
/usr/lib/python2.6/site-packages/ansible/runner/__init__.py 代码中的Runner类的run函数去执行
所以想要对Ansible的输出结果进一步处理,首先要获取Ansible调用命令执行的详细信息。
完整代码如下:
import ansible.runner from ansible.color import stringc host_list='qa_servers.txt' private_key_file='/root/.ssh/id_rsa' pattern='*' forks=10 timeout=30 module_name='shell' # construct the ansible runner and execute on all hosts results = ansible.runner.Runner( host_list=host_list, private_key_file=private_key_file, pattern=pattern, forks=forks, timeout=timeout, module_name=module_name, module_args=module_args ).run() #print results if results is None: print "No hosts found" sys.exit(1) print stringc("+-------------------+----------------------+-------------+-----------+--------+",color='cyan') print stringc("| HOST | HOSTNAME | CHICKED NUM | RIGHT NUM | STATUS |",color='cyan') print stringc("+-------------------+----------------------+-------------+-----------+--------+",color='cyan') for (hostname, result) in results['contacted'].items(): if not 'failed' in result: num_default=int(subprocess.Popen(''' awk '/%s/{print $3}' %s|sed -n 's/#//p' ''' %(hostname,host_list),shell=True,stdout=subprocess.PIPE).communicate()[0].split('\n')[0]) num=int(result['stdout'].split('\n')[0]) host_name=result['stdout'].split('\n')[1] if num==num_default: status=stringc('PASS',color='green') else: status=stringc('WARN',color='red') print stringc("| %-17s | %-20s | %-11d | %-9d | %-12s ",color='cyan') %(hostname,host_name,num,num_default,status) + stringc("|",color='cyan') print stringc("+-------------------+----------------------+-------------+-----------+--------+",color='cyan') # print "%s >>> %s" % (hostname, result['stdout']) #print "FAILED *******" #for (hostname, result) in results['contacted'].items(): # if 'failed' in result: # print "%s >>> %s" % (hostname, result['msg']) #print "DOWN *********" #for (hostname, result) in results['dark'].items(): # print "%s >>> %s" % (hostname, result)
qa_servers.txt的内容如下:
172.30.25.71 #testt #3 172.30.25.179 #testttttttt179 #30 172.30.25.180 #testttttttt180 #45 172.30.25.181 #testttttttt181 #55 172.30.25.172 #test #68 172.30.25.173 #test #7
第一列是需要执行命令的主机IP或者主机名,第二列是主机名,第三列就是正常的Java程序占用端口数量。第二列和第三列必须要注释掉。Ansible识别主机时只识别第一列。
执行代码的结果如下:
+-------------------+----------------------+-------------+-----------+--------+ | HOST | HOSTNAME | CHICKED NUM | RIGHT NUM | STATUS | +-------------------+----------------------+-------------+-----------+--------+ | 172.30.25.180 | testtesttesttt | 86 | 45 | WARN | +-------------------+----------------------+-------------+-----------+--------+ | 172.30.25.181 | testttttttt181 | 82 | 55 | WARN | +-------------------+----------------------+-------------+-----------+--------+ | 172.30.25.179 | testttttttt179 | 19 | 30 | WARN | +-------------------+----------------------+-------------+-----------+--------+ | 172.30.25.71 | testt | 3 | 3 | PASS | +-------------------+----------------------+-------------+-----------+--------+
代码中要点:
a.直接使用Ansible的代码进行模块调用。
b.使用Ansible的color.py对输出结果进行颜色处理,PASS是绿色显示,WARN是红色显示。这里的color.py很有启示作用,如果自己以后想要对输出进行颜色处理可以直接参照这里的代码
codeCodes = { 'black': '0;30', 'bright gray': '0;37', 'blue': '0;34', 'white': '1;37', 'green': '0;32', 'bright blue': '1;34', 'cyan': '0;36', 'bright green': '1;32', 'red': '0;31', 'bright cyan': '1;36', 'purple': '0;35', 'bright red': '1;31', 'yellow': '0;33', 'bright purple': '1;35', 'dark gray': '1;30', 'bright yellow': '1;33', 'normal': '0' } def stringc(text, color): """String in color.""" if ANSIBLE_COLOR: return "\033["+codeCodes[color]+"m"+text+"\033[0m" else: return text
c.使用python的subprocess模块调用shell命令
参考资料:
http://docs.ansible.com/ansible/developing_api.html