开发者

Python读取Ansible playbooks返回信息示例解析

目录
  • 一.背景及概要设计
  • Ansible playbooks 的返回信息
  • 二.表设计
    • 2.1 设计用来保存【最终执行结果】的表
    • 2.2 设计用来保存【各执行步骤详情】的表
  • 三.Models设计
    • AnsiblePlayRecap的定义
    • AnsibleTaskDetail的定义
  • 四.生成SQL脚本
    • 五. 主要功能代码
      • 5.1 执行ansible 命令
      • 5.2 开始逐行解析返http://www.devze.com回的结果
      • 5.3 获取playname 和taskname
      • 5.4 获取 paly 执行概况
      • 5.5 将paly 概况数据插入表中
      • 5.6 获取task执行情况,并将数据保存到表中
      • 5.7 去除干扰项和无效项
      • 5.8 补充有效项
    • 六. 其他说明

      一.背景及概要设计

      当公司管理维护的服务器到达一定规模后,就必然借助远程自动化运维工具,而ansible是其中备选之一。Ansible基于python开发,集合了众多运维工具(puppet、chef、func、fabric)的优点,实现了批量系统配置、批量程序部署、批量运行命令等功能。Ansible是借助ssh来和远程主机通讯的,不需要在远程主机上安装client/agents。因为上手容易,配置简单、功能强大、扩展性强,在生产应用中得到了广泛的应用。使用过程中,读取、解析、判断、保存Ansible playbooks 的执行返回信息是重要一坏。本文详细描述如何实现Python读取Ansible playbooks 执行返回信息,并且保存到数据库中。

      Ansible playbooks 的返回信息

      Ansible playbooks 的返回信息,有相应的格式。

      例如:

      PLAY [play to setup web server] *****************************************************

      TASK [Gathering Facts] **************************************************************

      ok: [172.177.117.129]

      ok: [172.177.117.130]

      TASK [Installed the latest httpd version] ***********************************************

      ok: [172.177.117.129]

      ok: [172.177.117.130]

      TASK [restart service] ***********************************************************

      changed: [172.177.117.129]

      changed: [172.177.117.130]

      PLAY RECAP **************************************************************************

      172.177.117.129 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

      172.177.117.130 : ok=3 changed=1www.devze.com unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

      从上面的例子可以看出,返回的运行信息还是很丰富的,从中可以得出play、task的名字、每个task执行情况,以及play运行情况的概况。

      即:

      When you run a playbook, Ansible returns information about connections, the name lines of all your plays and tasks, whether each task has succeeded or failed on each MAChine, and whether each task has made a change on each machine. At the bottom of the playbook execution, Ansible provides a summary of the nodes that were targeted and how they performed. General failures and fatal “unreachable” communication attempts are kept separate in the counts.

      重点及难点:从结果中找出规律,格式化结果,怎么用正则表达式取得想要的信息。

      二.表设计

      通过对Ansible playbooks返回信息的分析,可以将其分成两类(或者说两部分),一是play的整体执行情况(主要信息为PLAY RECAP ),另一个是每个task的执行详情。因此,我们设计了两张表。

      2.1 设计用来保存【最终执行结果】的表

      ansible_play_recap

      字段名字字段类型默认值COMMENT
      idbigint(20) NOT NULL AUTO_INCREMENT主键
      manager_ipvarchar(100) NOT NULL管理节点
      clusternamevarchar(200) NOT NULL集群名字
      playnamevarchar(360)NOT NULLAnsible剧本名称
      playrecap_serveripvarchar(50)NOT NULLAnsible运行节点(受管节点)
      playrecap_ok_qtyvarchar(10)NOT NULL成功运行的task个数
      playrecap_changed_qtyvarchar(10)NOT NULL产生效果的task个数
      playrecap_unreachablevarchar(10)NOT NULL相应的远程节点是否不可达
      playrecap_failed_qtyvarchar(10)NOT NULL执行失败的task个数【注意,不可达的情况,即未执行的情况下,失败的个数记为0,此时是为执行的】
      playrecap_skipped_qtyvarchar(10)NOT NULL跳过的task的个数
      playrecap_rescued_qtyvarchar(10)NOT NULL抢救的task的个数
      playrecap_ignored_qtyvarchar(10)NOT NULL忽略的task的个数
      create_timedatetime(6) NOT NULL插入时间
      create_uservarchar(50)NOT NULL操作人

      2.2 设计用来保存【各执行步骤详情】的表

      ansible_task_palydetail

      字段名字字段类型默认值COMMENT
      idbigint(20) NOT NULL AUTO_INCREMENT主键
      manager_ipvarchar(100) NOT NULL管理节点
      clusternamevarchar(200) NOT NULL集群名字
      playnamevarchar(360)NOT NULLAnsible剧本名称
      task_serveripvarchar(50)NOT NULLAnsible运行节点(受管节点)
      tasknamevarchar(360)NOT NULL任务名称
      task_statusvarchar(50)NOT NULL任务执行结果
      task_result_typevarchar(10)NOT NULL执行结果类型(错误类型)
      task_messagesmediumtextNOT NULLTask运行返回信息(错误信息)
      create_timedatetime(6) NOT NULL插入时间
      create_uservarchar(50)NOT NULL操作人

      注意:

      (1)可以根据需要,在表中增加一列ansible_cmd,用来保存执行的ansible的命令。

      (2)为什么会有看着奇怪的manager_ip、clustername?因为,这份代码来自于对DB 集群的 部署 和 管理,可根据实际需要,修改取舍(即你的代码可以把他们去掉)。

      三.Models设计

      AnsiblePlayRecap的定义

      class AnsiblePlayRecap(models.Model):
          """
          保存ansible最终执行结果的表
          """
          id = models.AutoField('自增id', primary_key=True)
          manager_ip = models.CharField('MHA Manager IP', max_length=100)
          clustername = models.CharField('HA 集群名字', max_length=200, default='')
          playname = models.CharField('Ansible剧本名称', max_length=360, default='')
          playrecap_serverip = models.CharField('受管节点', max_length=50, default='')
          playrecap_ok_qty = models.CharField('此节点成功运行的task个数', max_length=10, default='')
          playrecap_changed_qty = models.CharField('产生效果的task个数', max_length=10, default='')
          playrecap_unreachable = models.CharField('相应的远程节点是否不可达', max_length=10, default='')
          playrecap_failed_qty = models.CharField('执行失败的task个数', max_length=10, default='')
          playrecap_skipped_qty = models.CharField('跳过的task的个数', max_length=10, default='')
          playrecap_rescued_qty = models.CharField('抢救的task的个数', max_length=10, default='')
          playrecap_ignored_qty = models.CharField('忽略的task的个数', max_length=10, default='')
          create_time = models.DateTimeField('插入时间', auto_now=True)
          create_user = models.CharField('操作人', max_length=50, default='')
      
          class Meta:
              db_table = 'ansible_play_recap'
              verbose_name = '保存ansible最终执行结果的表'

      AnsibleTaskDetail的定义

      class AnsibleTaskDetail(models.Model):
          """
          保存各task执行详情的表
          """
          id = models.AutoField('自增id', primary_key=True)
          manager_ip = models.CharField('MHA Manager IP', max_length=100)
          clustername = models.CharField('HA 集群名字', max_length=200, default='')
          playname = models.CharField('Ansible剧本名称', max_length=360, default='')
          task_serverip = models.CharField('受管节点', max_length=50, default='')
          taskname = models.CharField('任务名称', max_length=360, default='')
          task_status = models.CharField('任务执行结果', max_length=50, default='')
          task_result_type = models.CharField('执行结果的错误类型', max_length=10, default='')
          task_messages = models.TextField('Task运行返回信息')
          create_time = models.DateTimeField('插入时间', auto_now=True)
          create_user = models.CharField('操作人', max_length=50, default='')
      
          class Meta:
              db_table = 'ansible_task_palydetail'
              verbose_name = '保存各执行步骤详情的表'

      四.生成SQL脚本

      由model所在的项目名称,通过运行 python manage.py生成

      假如项目名称用XXXX代替

      ---生成脚本

      python manage.py makemigrations XXXX

      ---显示刚才生成的SQL脚本(0006为版本序列号)

      python manage.py sqlmigrate XXXX 0006

      五. 主要功能代码

      调用代码,需传入的参数有三个,

      (1)shell_command 餐宿 -----即要执行的Ansible Playbook 命令;

      (2)manager_ip参数

      (3)cluster_name 参数--- 这两个命令前面已解释了,因为我们的这份代码,其功能是为了维护数据库集群的。在其他场景下,这两个参数可以去掉。

      5.1 执行ansible 命令

      声明关于正则的模式;连接远程ansible主机;获取ansible 执行结果;

      from .ansible import ParamikoHelper
          ##paramiko 是一个用于在Python中执行远程操作的模块,支持SSH协议。它可以用于连接到远程服务器,执行命令、上传和下载文件,以及在远程服务器上执行各种操作。
          ##字符串中关于IP地址的正则表达式
          ## ^:匹配字符串的开头。((25[0-5]|2[0-4]\d|[01]?\d\d?)\.):匹配一个数字和一个点号,这个数字的取值范围是0到255。
          ## {3}:匹配前面的表达式三次。(25[0-5]|2[0-4]\d|[01]?\d\d?):   配一个数字,这个数字的取值范围是0到255。$:匹配字符串的结尾。
          ## 使用正则表达式匹配IP地址
          # 字符串是IP地址
          ip_pattern = r'^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$'
          ##字符串是IP地址开头的
          ipstart_pattern = r'^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)'
          ##字符串包含IP
          ipcontain_pattern = r'((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)'   
          ##字符串包含IP,并且IP地址是以': ['字符开头,以']'字符结尾
          ipcontain_pattern_plus = r'(\: \[)((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}((25[0-5]|2[0-4]\d|[01]?\d\d?)\])' 
          ansible_ip = '你的ansible server IP'
          ssh_port = 你的ssh_port
          ssh_username = '免密登录设置的账号'
          ph = ParamikoHelper(remote_ip=ansible_ip,remote_ssh_port=ssh_port,ssh_username=ssh_username)
          stdin, stdout, stderr = ph.exec_shell(shell_command)
          processor_result = stdout.readlines() #readlines()列表形式返回全文,每行作为一个字符串作为列表元素

      5.2 开始逐行解析返回的结果

      先判断这一行是否以Server IP开头(是的话,就是 PLAY RECAP 中的内容 ),

      还要判断这一行 是否 有 以': ['字符开头,以']'字符结束的Server IP(如果是的话,很可能就是task部分的内容)

      两个判断是各自独立的,相互没有关系依赖。

      ### 先赋值,否则有可能报错:UnboundLocalError: local variable 'XXXXX' referenced before assignment
          rplayname = ''
          rtask_result_type =''
          ###
          for pr_line in processor_result:
            logger.warning(f'{pr_line}')
            ## 判断这个字符串是不是以IP地址开头
            ip_result = re.search(ipstart_pattern, pr_line)
            ## 判断这个字符串是不是包含IP地址,并且IP以': ['字符开头,以']'字符结束  
            ip_plus_result = re.search(ipcontain_pattern_plus, pr_line) 
            ##获取playname start

      5.3 获取playname 和taskname

      根据是否含有'PLAY ['字符、'TASK ['字符进行判断和提取。

      ##获取playname
            if 'PLAY [' in pr_line:
              ##使用的正则表达式'\[(.*?)\]',其中'\'为转移符,用于表示左右中括号的匹配,'?'表示非贪婪模式,这个模式会匹配最短的符合要求的字符串。
              ## [0],因正则匹配后,放回的是数组,通过[0],转换为字符串。
              rplayname = re.findall(r'\[(.*?)\]', pr_line)[0]##获取task 的名称
            elif 'TASK [' in pr_line:
              rtaskname = re.findall(r'\[(.*?)\]', pr_line)[0]

      5.4 获取 paly 执行概况

      即PLAY RECAP 部分内容,主要依据是这行的字符是以IP地址开头的。 

      ## 判断这个字符串是不是以IP地址开头
            elif ip_result: #字符串是IP地址开头的
              ## 此时pr_line的字符串格式如下:
              ## pr_line = '172.173.17.18               : ok=5    changed=2    unreachable=1    failed=0    skipped=6    rescued=7    ignored=8'
              rserverip = ip_result.group() ## 匹配的server IP
              ## print(rserverip) ##打印IP地址
      
              ## 正则表达式,\s+ ,将一个以多个空格或制表符为分隔符的字符串拆分成一个列表
              pr_line_lst = re.split(r"\s+", pr_line)
              ##分割后为: ['172.173.17.18', ':', 'ok=5', 'changed=2', 'unreachable=1', 'failed=0', 'skipped=6', 'rescued=7', 'ignored=8']
              for pr_arry in pr_line_lst:
                if 'ok=' in pr_arry:
                  rplayrecap_ok_qty = pr_arry.split("ok=")[1] ##记得:字符串切割后返回的是数组,所以取第二个元素if 'changed=' in pr_arry:
                  rplayrecap_changed_qty = pr_arry.split("changed=")[1]if 'unreachable=' in pr_arry:
                  rplayrecap_unreachable = pr_arry.split("unreachable=")[1]if 'failed=' in pr_arry:
                  rplayrecap_failed_qty = pr_arry.split("failed=")[1]if 'skipped=' in pr_arry:
                  rplayrecap_skipped_qty = pr_arry.split("skipped=")[1]if 'rescued=' in pr_arry:
                  rplayrecap_rescued_qty = pr_arry.split("rescued=")[1]if 'ignored=' in pr_arry:
                  rplayrecap_ignored_qty = pr_arry.split("ignored=")[1]

      5.5 将paly 概况数据插入表中

      Django 框架,关于Model数据的写入。

      ### 开始向表[ansible_play_recap]中插入数据,保存ansible最终执行结果的表
              AnsiblePlayRecap.objects.create(manager_ip=manager_ip,clustername=cluster_name,playname=rplayname,playrecap_serverip=rserverip,
                                                          playrecap_okandroid_qty=rplayrecap_ok_qty,playrecap_changed_qty=rplayrecap_changed_qty,
                                                          playrecap_unreachable=rplayrecap_unreachable,playrecap_failed_qty=rplayrecap_failed_qty,
                                                          playrecap_skipped_qty=rplayrecap_skipped_qty,playrecap_rescued_qty=rplayrecap_rescued_qty,
                                                          playrecap_ignored_qty=rplayrecap_ignored_qty, create_user='Archery System'
                                                          )

      5.6 获取task执行情况,并将数据保存到表中

      如果这一行数据包含Server IP地址,并且这个 IP以': ['字符开头,以']'字符结尾的,那么这行记录的就是这个task在某受管节点的执行情况。

      ## 判断这个字符串是不是包含IP地址,并且IP以': ['字符开头,以']'字符结尾
            elif ip_plus_result: ##字符串包含IP,并且IP地址是以': ['字符开头,以']'字符结尾
              if 'ok: [' in pr_line:
                rtask_status = 'ok'
                rtask_messages = '' ## 赋值空
                ##查找server IP
                result = re.search(ipcontain_pattern, pr_linejavascript)
                rserverip = result.group() ## 匹配的server IP  
                ## print(rserverip)
              elif 'changed: [' in pr_line:
                rtask_status = 'changed'
                rtask_messages = '' ## 赋值空##查找server IP
                result = re.search(ipcontain_pattern, pr_line)
                rserverip = result.group() ## 匹配的server IP## 有些 返回的change 中还有其他信息,例如:changed: [192.168.168.192] => (item=perl-Parallel-ForkManager-1.18-2.el7.noarch.rpm)
                ## 此时判断下,是否包含 '] =>',如果包含,赋值给  
                if '] => ' in pr_line:
                  rtask_messages= pr_line.split("] => ")[1]elif 'skipping: [' in pr_line:
                rtask_status = 'skipping'
                rtask_messages = '' ## 赋值空
                ##查找server IP
                result = re.search(ipcontain_pattern, pr_line)
                rserverip = result.group() ## 匹配的server IPelif 'fatal: [' in pr_line:
                rtask_status = 'fatal'
                rtask_messages = '' ## 赋值空
                rtask_result_type ='FAILED'##查找server IP
                result = re.search(ipcontain_pattern, pr_line)
                rserverip = result.group() ## 匹配的server IPif 'FAILED! =>' in pr_line:
                  rtask_messages= pr_line.split("FAILED! =>")[1]else:
                rtask_status = 'NA'
                rtask_messages = '未知状态,请DBAcheck......' + pr_line
              ### 开始向表中插入数据
              AnsibleTaskDetail.objects.create(manager_ip=manager_ip,clustername=cluster_name,playname=rplayname,playrecap_serverip=rserverip,
                                                          taskname=rtaskname,task_status=rtask_status,
                                                          task_result_type=rtask_result_type,task_messages=rtask_messages,
                                                          create_user='Archery System'
                                                          )

      5.7 去除干扰项和无效项

      elif len(pr_line) == 0 or pr_line == '\n' or ('PLAY RECAP *******' in  pr_line): ###判断是否空 或只是 简单的换行符,再或者包含指定字符
                 print("这一行为空行 或 说明行,无需记录!")

      5.8 补充有效项

      当执行task返回OK时,,后面跟个IP,再后面一般不跟啥了;但是有时候还会由跟东西的。啥时候跟呢? 

      例如:pythontask   #debug:  #   msg: "你想要的返回信息。。。。。。"  这类命令时。

      else:
              rtask_status = 'Mostly OK'
              rtask_result_type = 'debug+msg'   ##'经常出现在task中有debug:msg:的时候'
              rtask_messages =  pr_line
              ### 开始向表中插入数据
              AnsibleTaskDetail.objects.create(manager_ip=manager_ip,clustername=cluster_name,playname=rplayname,playrecap_serverip=rserverip,
                                                          taskname=rtaskname,task_status=rtask_status,
                                                          task_result_type=rtask_result_type,task_messages=rtask_messages,
                                                          create_user='Archery System'
                                                          )
      
              ###这段处理的情形不好想像,比较难懂,举个例子
              ## ok: [192.168.168.192] => 
              ##  {
              ##     "msg": "mysql Replication Health is OK!"
              ## }
              ##需要注意的时,相应的在表中也会保留多行数据。因为我们时逐行获取,逐行解析,逐行报错的。不过庆幸的时,顺序都是对的。

      六. 其他说明

      必须说明的是:上面的Python代码针对的是ansible host 文件保存的是Server IP,如果是域名,那么关于IP的正则是不可用的,代码必须调整。

      补充几个task的返回信息的示例,方便理解代码。

      示例 1

      ok: [192.168.168.192] => {\n', '    "msg": "MySQL Replication Health is OK!"\n', '}\n

      示例 2

      changed: [192.168.168.192] => (item=perl-Parallel-ForkManager-1.18-2.el7.noarch.rpm)

      示例 3

      fatal: [192.168.168.192]: FAILED! => {"changed": false, "msg": "No package matching "test" found available, installed or updated", "rc": 126, "results": ["No package matching "test" found available, installed or updated']}""" 

      示例 4

      skipping: [192.168.168.192]

      示例 5

      changed: [192.168.168.192]

      示例 6 

      ok: [192.168.168.192]

      Python读取Ansible playbooks返回信息只是平台的一个小功能,整个系统平台采用的是Django框架。

      以上就是Python读取Ansible playbooks返回信息示例解析的详细内容,更多关于Python读取Ansible playbooks的资料请关注编程客栈(www.devze.com)其它相关文章!

      0

      上一篇:

      下一篇:

      精彩评论

      暂无评论...
      验证码 换一张
      取 消

      最新开发

      开发排行榜