一 应用场景描述

有这个么一个需求:对很多台服务器的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



转载于:https://blog.51cto.com/lucifer119/1733750