黑帽联盟

 找回密码
 会员注册
查看: 1175|回复: 0
打印 上一主题 下一主题

[基础服务] ansible笔记(32):过滤器(二)

[复制链接]
yun 黑帽联盟官方人员 

920

主题

37

听众

1364

积分

超级版主

Rank: 8Rank: 8

  • TA的每日心情
    奋斗
    2019-10-18 11:20
  • 签到天数: 678 天

    [LV.9]以坛为家II

    本帖最后由 yun 于 2019-9-11 17:16 编辑

    ansible是一个系列文章,我们会尽量以通俗易懂的方式总结ansible的相关知识点。
    ansible系列博文直达链接:ansible轻松入门系列
    "ansible系列"中的每篇文章都建立在前文的基础之上,所以,请按照顺序阅读这些文章,否则有可能在阅读中遇到障碍。


    之前已经总结过一些过滤器的用法,这篇文章我们继续来认识一些其他的过滤器。

    在实际工作中,经常需要调用别人的接口,别人的接口会返回我们需要的数据,有时候,返回的数据就是json格式的,比如,公司为某些域名配置了CDN,当我们需要分析这些域名的访问日志时,则需要从CDN厂商获取到对应的日志,通常情况下,CDN服务商会暴露一个接口给我们,我们调用这个接口,即可获取到对应域名的日志列表,然后根据列表下载所有的日志,从而达到我们的目的,我们先一起来看一段示例数据,如下数据是我调用了CDN厂商接口后,返回的一段json格式的数据,如下
    1. {"logs":[{"domainName":"asia1.cdn.test.com","files":[{"dateFrom":"2018-09-05-0000","dateTo":"2018-09-05-2359","logUrl":"http://log.testcd.com/log/zsy/asia1.cdn.test.com/2018-09-05-0000-2330_asia1.cdn.test.com.all.log.gz?wskey=XXXXX5a","fileSize":254,"fileName":"2018-09-05-0000-2330_asia1.cdn.test.com.all.log.gz","fileMd5":"error"}]},{"domainName":"image1.cdn.test.com","files":[{"dateFrom":"2018-09-05-2200","dateTo":"2018-09-05-2259","logUrl":"http://log.testcd.com/log/zsy/image1.cdn.test.com/2018-09-05-2200-2230_image1.cdn.test.com.cn.log.gz?wskey=XXXXX1c","fileSize":10509,"fileName":"2018-09-05-2200-2230_image1.cdn.test.com.cn.log.gz","fileMd5":"error"},{"dateFrom":"2018-09-05-2300","dateTo":"2018-09-05-2359","logUrl":"http://log.testcd.com/log/zsy/image1.cdn.test.com/2018-09-05-2300-2330_image1.cdn.test.com.cn.log.gz?wskey=XXXXXfe","fileSize":5637,"fileName":"2018-09-05-2300-2330_image1.cdn.test.com.cn.log.gz","fileMd5":"error"}]}]}
    复制代码
    这段json数据并没有很高的可读性,因为它并没有进行任何缩进和换行,如果你想让这段数据有更高的可读性,则可以将这段数据所在的文件当做变量文件引入到playbook中,然后输出对应的变量即可,示例如下
    1. ---
    2. - hosts: test70
    3.   remote_user: root
    4.   gather_facts: no
    5.   tasks:
    6.   - include_vars:
    7.       file: "/testdir/ansible/wsCdnLogList"
    8.       name: testvar
    9.   - debug:
    10.       msg: "{{ testvar }}"
    复制代码
    如上例所示,"/testdir/ansible/wsCdnLogList"就是上述json数据所在的文件,它位于ansible主机中,我们使用include_vars模块,将此文件当做变量文件引入了playbook中,在总结变量时我们提到过,变量文件的格式可以是yaml格式的,也可以是json格式的,上例就是将json格式的数据文件当做变量文件使用的,并且,将此文件的json数据赋值给了testvar变量,然后使用debug模块输出了testvar变量的值,那么,我们执行一下上例的playbook,可以发现,json数据已经被格式化为了有一定可读性的json文本输出在了控制台中,输出信息如下:
    1. TASK [debug] ********************************************************************
    2. ok: [test70] => {
    3.     "msg": {
    4.         "logs": [
    5.             {
    6.                 "domainName": "asia1.cdn.test.com",
    7.                 "files": [
    8.                     {
    9.                         "dateFrom": "2018-09-05-0000",
    10.                         "dateTo": "2018-09-05-2359",
    11.                         "fileMd5": "error",
    12.                         "fileName": "2018-09-05-0000-2330_asia1.cdn.test.com.all.log.gz",
    13.                         "fileSize": 254,
    14.                         "logUrl": "http://log.testcd.com/log/zsy/asia1.cdn.test.com/2018-09-05-0000-2330_asia1.cdn.test.com.all.log.gz?wskey=XXXXX5a"
    15.                     }
    16.                 ]
    17.             },
    18.             {
    19.                 "domainName": "image1.cdn.test.com",
    20.                 "files": [
    21.                     {
    22.                         "dateFrom": "2018-09-05-2200",
    23.                         "dateTo": "2018-09-05-2259",
    24.                         "fileMd5": "error",
    25.                         "fileName": "2018-09-05-2200-2230_image1.cdn.test.com.cn.log.gz",
    26.                         "fileSize": 10509,
    27.                         "logUrl": "http://log.testcd.com/log/zsy/image1.cdn.test.com/2018-09-05-2200-2230_image1.cdn.test.com.cn.log.gz?wskey=XXXXX1c"
    28.                     },
    29.                     {
    30.                         "dateFrom": "2018-09-05-2300",
    31.                         "dateTo": "2018-09-05-2359",
    32.                         "fileMd5": "error",
    33.                         "fileName": "2018-09-05-2300-2330_image1.cdn.test.com.cn.log.gz",
    34.                         "fileSize": 5637,
    35.                         "logUrl": "http://log.testcd.com/log/zsy/image1.cdn.test.com/2018-09-05-2300-2330_image1.cdn.test.com.cn.log.gz?wskey=XXXXXfe"
    36.                     }
    37.                 ]
    38.             }
    39.         ]
    40.     }
    41. }
    复制代码
    其实,json是yaml的子集,yaml是json的超集,yaml格式的数据和json格式的数据是可以互相转换的,所以,对于ansible来说,当我们把上例中的json数据文件当做变量文件引入时,就好像引入了一个我们定义好的yaml格式的变量文件一样,对于ansible来说是没有区别的,而且,即使在变量文件中使用yaml格式定义了变量,在使用debug模块输出变量信息时,ansible也会自动将yaml格式的数据转化为json格式后进行输出,细心如你一定已经在前面的文章中发觉到这一点了,那么,我们把上述json数据转换成yaml的格式,同时列出上述数据的json格式与yaml格式,你可以根据自己的使用习惯,选择阅读哪种格式的数据,以便你能够更好的理解这段数据的含义,yaml格式如下
    1. logs:
    2. -   domainName: asia1.cdn.test.com
    3.     files:
    4.     -   dateFrom: 2018-09-05-0000
    5.         dateTo: 2018-09-05-2359
    6.         fileMd5: error
    7.         fileName: 2018-09-05-0000-2330_asia1.cdn.test.com.all.log.gz
    8.         fileSize: 254
    9.         logUrl: http://log.testcd.com/log/zsy/asia1.cdn.test.com/2018-09-05-0000-2330_asia1.cdn.test.com.all.log.gz?wskey=XXXXX5a
    10. -   domainName: image1.cdn.test.com
    11.     files:
    12.     -   dateFrom: 2018-09-05-2200
    13.         dateTo: 2018-09-05-2259
    14.         fileMd5: error
    15.         fileName: 2018-09-05-2200-2230_image1.cdn.test.com.cn.log.gz
    16.         fileSize: 10509
    17.         logUrl: http://log.testcd.com/log/zsy/image1.cdn.test.com/2018-09-05-2200-2230_image1.cdn.test.com.cn.log.gz?wskey=XXXXX1c
    18.     -   dateFrom: 2018-09-05-2300
    19.         dateTo: 2018-09-05-2359
    20.         fileMd5: error
    21.         fileName: 2018-09-05-2300-2330_image1.cdn.test.com.cn.log.gz
    22.         fileSize: 5637
    23.         logUrl: http://log.testcd.com/log/zsy/image1.cdn.test.com/2018-09-05-2300-2330_image1.cdn.test.com.cn.log.gz?wskey=XXXXXfe
    复制代码
    从上述信息可以看出,cdn厂商返回的j数据是一个日志列表,这个日志列表中一共有两个对象,对象的属性有"domainName"和 "files",很明显,cdn厂商将日志按照域名进行了划分,返回了对应域名下的所有日志, "files"属性是一个列表,列表中列出了对应域名中的所有日志文件的信息,第一个域名的files列表中只有一个日志文件,第二个域名的files列表中一共有两个日志文件。

    此刻,如果我们想要获取到整个列表中的所有日志文件的logUrl,我们该怎么办呢?
    没错,聪明如你一定想到了,我们可以通过with_subelements循环,获取到当前整个大列表中的所有日志的logUrl属性,前文已经总结过了with_subelements的用法,此处不再赘述,如果你忘记了它的用法,请回顾前文,此处直接写出示例,如下:
    1.   tasks:
    2.   - include_vars:
    3.       file: "/testdir/ansible/wsCdnLogList"
    4.       name: testvar
    5.   - debug:
    6.       msg: "{{ item.1.logUrl }}"
    7.     with_subelements:
    8.     - "{{testvar.logs}}"
    9.     - files
    复制代码
    其实,除了使用with_subelements,我们还有另外一种方法,就是使用过滤器,有一个名为json_query的过滤器,可以帮助我们解决上述问题。

    我们先来看几个关于json_query的小示例,等熟悉了json_query过滤器以后,再回过头来通过json_query解决上面的问题,小示例如下
    假设我们现在有一段简单的json数据,如下:
    1. {
    2.   "users": [
    3.     {
    4.       "name": "tom",
    5.       "age": 18
    6.     },
    7.     {
    8.       "name": "jerry",
    9.       "age": 20
    10.     }
    11.   ]
    12. }
    复制代码
    上述json数据的yaml格式如下:
    1. ---
    2. users:
    3. - name: tom
    4.   age: 18
    5. - name: jerry
    6.   age: 20
    复制代码
    从上述示例可以看出,一共有两个用户,两个用户的名字分别为tom和jerry,年龄分别为18和20,如果我们想要通过json_query过滤器获取到上述json中的所有user的name,则可以使用如下方法:
    1. ---
    2. - hosts: test70
    3.   remote_user: root
    4.   gather_facts: no
    5.   tasks:
    6.   - include_vars:
    7.       file: "/testdir/ansible/testvarfile"
    8.       name: testvar
    9.   - debug:
    10.       msg: "{{ testvar | json_query('users[*].name') }}"
    复制代码
    上例的"/testdir/ansible/testvarfile"文件就是上述示例数据所在的文件,它位于ansible主机中,我们将这段数据当做变量赋值给了testvar变量,之后,使用json_query过滤器对这个变量进行了处理,json_query('users.name')表示找到users列表中所有元素的name属性,执行上例playbook后,debug模块的输出信息如下:
    1. TASK [debug] *****************************************
    2. ok: [test70] => {
    3.     "msg": [
    4.         "tom",
    5.         "jerry"
    6.     ]
    7. }
    复制代码
    如我们所愿,我们找到了所有用户的name属性,它们被放在了一个列表中展示了出来,你一定已经会举一反三了,查找user列表中的所有元素的age属性的值,则可以使用如下方法
    1.   - debug:
    2.       msg: "{{ testvar | json_query('users[*].age') }}"
    复制代码
    我们再来看一段数据,这段数据位于ansible主机的/testdir/ansible/testvarfile1文件中,此处不再将如下yaml数据转换成json格式,在如下示例中,两种格式没有任何区别,示例数据如下
    1. ---
    2. test:
    3.   users:
    4.   - name: tom
    5.     age: 18
    6.     hobby:
    7.     - Skateboard
    8.     - VideoGame
    9.   - name: jerry
    10.     age: 20
    11.     hobby:
    12.     - Music
    复制代码
    从上述示例可以看出,一共有两个用户,除了姓名和年龄属性,每个用户还有爱好属性,爱好属性是一个列表,如果,我们想要获取到所有的爱好,改怎样使用json_query来完成呢?示例如下:
    1. ---
    2. - hosts: test70
    3.   remote_user: root
    4.   gather_facts: no
    5.   tasks:
    6.   - include_vars:
    7.       file: "/testdir/ansible/testvarfile1"
    8.       name: testvar
    9.   - debug:
    10.       msg: "{{ testvar | json_query('test.users[*].hobby[*]') }}"
    复制代码
    没错,上例表示,获取到users列表中所有hobby列表的所有项,正如你所看到的,当数据结构中存在列表时,我们可以使用"列表名"获取到列表下面的所有项,那么,我们来执行一下上例的playbook,执行后debug模块输出结果如下:
    1. TASK [debug] *************************************
    2. ok: [test70] => {
    3.     "msg": [
    4.         [
    5.             "Skateboard",
    6.             "VideoGame"
    7.         ],
    8.         [
    9.             "Music"
    10.         ]
    11.     ]
    12. }
    复制代码
    返回给我们的数据是一个json列表,这个列表中嵌套了两个列表,正是上述示例数据中两个用户的"爱好列表",如果你想要将上例嵌套的列表拉平后输出,则可以使用之前总结的循环进行操作,此处不再赘述。

    在上述示例中,我们都是获取到users列表中的所有用户的信息,但是如果我们想要根据条件获取到某个用户的某些信息,该怎么办呢?比如,我想要获取到tom的爱好,则可以使用如下方法:
    1.   tasks:
    2.   - include_vars:
    3.       file: "/testdir/ansible/testvarfile1"
    4.       name: testvar
    5.   - debug:
    6.       msg: "{{ testvar | json_query('test.users[?name==`tom`].hobby[*]') }}"
    复制代码
    上例中,json_query('test.users[?name==`tom`].hobby')表示只查找users列表中name属性等于tom的hobby信息,你肯定发现了,上例中,我们使用了"反引号"将tom引起来了,这是因为当你在添加查询条件时,对需要为对应的值添加引号,但是,上例中msg的值的外侧已经存在一对双引号,双引号内又有一对单引号,所以,为了不让引号之间互相冲突,此处使用反引号将tom引起,除了使用反引号,也可以如下方法,实现相同的效果:
    1.   tasks:
    2.   - include_vars:
    3.       file: "/testdir/ansible/testvarfile1"
    4.       name: testvar
    5.   - debug:
    6.       msg: "{{ testvar | json_query(querystring) }}"
    7.     vars:
    8.       querystring: "test.users[?name=='tom'].age"
    复制代码
    如上例所示,我们在debug任务中使用vars关键字定义了一个只有当前debug任务能够使用的变量,从而避免了多层引号嵌套时所产生的冲突问题。

    我们也可以同时获取到用户的姓名、年龄两个属性的值,当需要同时获取多个属性值时,需要通过键值对的方式调用属性,示例如下:
    1.   tasks:
    2.   - include_vars:
    3.       file: "/testdir/ansible/testvarfile1"
    4.       name: testvar
    5.   - debug:
    6.       msg: "{{ testvar | json_query('test.users[*].{uname:name,uage:age}') }}"
    复制代码
    如上例所示 ,json_query('test.users.{uname:name,uage:age}')表示找到users列表中所有用户的name值和age值,如你所见,uname和uage是我自定义的名称,你也可以随意定义,但是,name和age必须用数据中的属性的键名对应,执行上例playbook后,debug模块输出信息如下:
    1. TASK [debug] **************************************
    2. ok: [test70] => {
    3.     "msg": [
    4.         {
    5.             "uage": 18,
    6.             "uname": "tom"
    7.         },
    8.         {
    9.             "uage": 20,
    10.             "uname": "jerry"
    11.         }
    12.     ]
    13. }
    复制代码
    如上所示,用户的姓名和年龄都按照我们定义的方式输出了。

    好了,说了这么多,json_query过滤器的用法你肯定已经掌握了,所以,我们回过头来,使用json_query来解决一下文章最开始的问题,我们的目标是,使用json_query过滤器找到cdn厂商返回的json数据中的所有日志的logUrl,那么我们可以怎么编写playbook呢?示例如下 :
    1. ---
    2. - hosts: test70
    3.   remote_user: root
    4.   gather_facts: no
    5.   vars_files:
    6.   - /testdir/ansible/wsCdnLogList
    7.   tasks:
    8.   - debug:
    9.       msg: "{{item}}"
    10.     with_items: "{{ logs | json_query('[*].files[*].logUrl') }}"
    复制代码
    上例playbook的执行效果我就不粘贴了,快动手试试吧。

    希望这篇文章能够帮助到你,加油~

    帖子永久地址: 

    黑帽联盟 - 论坛版权1、本主题所有言论和图片纯属会员个人意见,与本论坛立场无关
    2、本站所有主题由该帖子作者发表,该帖子作者与黑帽联盟享有帖子相关版权
    3、其他单位或个人使用、转载或引用本文时必须同时征得该帖子作者和黑帽联盟的同意
    4、帖子作者须承担一切因本文发表而直接或间接导致的民事或刑事法律责任
    5、本帖部分内容转载自其它媒体,但并不代表本站赞同其观点和对其真实性负责
    6、如本帖侵犯到任何版权问题,请立即告知本站,本站将及时予与删除并致以最深的歉意
    7、黑帽联盟管理员和版主有权不事先通知发贴者而删除本文

    您需要登录后才可以回帖 登录 | 会员注册

    发布主题 !fastreply! 收藏帖子 返回列表 搜索
    回顶部