黑帽联盟

标题: ansible笔记(32):过滤器(二) [打印本页]

作者: yun    时间: 2019-9-10 13:18
标题: ansible笔记(32):过滤器(二)
本帖最后由 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的执行效果我就不粘贴了,快动手试试吧。

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






欢迎光临 黑帽联盟 (https://bbs.cnblackhat.com/) Powered by Discuz! X2.5