本帖最后由 yun 于 2019-9-11 17:16 编辑
ansible是一个系列文章,我们会尽量以通俗易懂的方式总结ansible的相关知识点。 "ansible系列"中的每篇文章都建立在前文的基础之上,所以,请按照顺序阅读这些文章,否则有可能在阅读中遇到障碍。
之前已经总结过一些过滤器的用法,这篇文章我们继续来认识一些其他的过滤器。
在实际工作中,经常需要调用别人的接口,别人的接口会返回我们需要的数据,有时候,返回的数据就是json格式的,比如,公司为某些域名配置了CDN,当我们需要分析这些域名的访问日志时,则需要从CDN厂商获取到对应的日志,通常情况下,CDN服务商会暴露一个接口给我们,我们调用这个接口,即可获取到对应域名的日志列表,然后根据列表下载所有的日志,从而达到我们的目的,我们先一起来看一段示例数据,如下数据是我调用了CDN厂商接口后,返回的一段json格式的数据,如下 - {"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中,然后输出对应的变量即可,示例如下 - ---
- - hosts: test70
- remote_user: root
- gather_facts: no
- tasks:
- - include_vars:
- file: "/testdir/ansible/wsCdnLogList"
- name: testvar
- - debug:
- msg: "{{ testvar }}"
复制代码如上例所示,"/testdir/ansible/wsCdnLogList"就是上述json数据所在的文件,它位于ansible主机中,我们使用include_vars模块,将此文件当做变量文件引入了playbook中,在总结变量时我们提到过,变量文件的格式可以是yaml格式的,也可以是json格式的,上例就是将json格式的数据文件当做变量文件使用的,并且,将此文件的json数据赋值给了testvar变量,然后使用debug模块输出了testvar变量的值,那么,我们执行一下上例的playbook,可以发现,json数据已经被格式化为了有一定可读性的json文本输出在了控制台中,输出信息如下: - TASK [debug] ********************************************************************
- ok: [test70] => {
- "msg": {
- "logs": [
- {
- "domainName": "asia1.cdn.test.com",
- "files": [
- {
- "dateFrom": "2018-09-05-0000",
- "dateTo": "2018-09-05-2359",
- "fileMd5": "error",
- "fileName": "2018-09-05-0000-2330_asia1.cdn.test.com.all.log.gz",
- "fileSize": 254,
- "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"
- }
- ]
- },
- {
- "domainName": "image1.cdn.test.com",
- "files": [
- {
- "dateFrom": "2018-09-05-2200",
- "dateTo": "2018-09-05-2259",
- "fileMd5": "error",
- "fileName": "2018-09-05-2200-2230_image1.cdn.test.com.cn.log.gz",
- "fileSize": 10509,
- "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"
- },
- {
- "dateFrom": "2018-09-05-2300",
- "dateTo": "2018-09-05-2359",
- "fileMd5": "error",
- "fileName": "2018-09-05-2300-2330_image1.cdn.test.com.cn.log.gz",
- "fileSize": 5637,
- "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"
- }
- ]
- }
- ]
- }
- }
复制代码其实,json是yaml的子集,yaml是json的超集,yaml格式的数据和json格式的数据是可以互相转换的,所以,对于ansible来说,当我们把上例中的json数据文件当做变量文件引入时,就好像引入了一个我们定义好的yaml格式的变量文件一样,对于ansible来说是没有区别的,而且,即使在变量文件中使用yaml格式定义了变量,在使用debug模块输出变量信息时,ansible也会自动将yaml格式的数据转化为json格式后进行输出,细心如你一定已经在前面的文章中发觉到这一点了,那么,我们把上述json数据转换成yaml的格式,同时列出上述数据的json格式与yaml格式,你可以根据自己的使用习惯,选择阅读哪种格式的数据,以便你能够更好的理解这段数据的含义,yaml格式如下 - logs:
- - domainName: asia1.cdn.test.com
- files:
- - dateFrom: 2018-09-05-0000
- dateTo: 2018-09-05-2359
- fileMd5: error
- fileName: 2018-09-05-0000-2330_asia1.cdn.test.com.all.log.gz
- fileSize: 254
- 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
- - domainName: image1.cdn.test.com
- files:
- - dateFrom: 2018-09-05-2200
- dateTo: 2018-09-05-2259
- fileMd5: error
- fileName: 2018-09-05-2200-2230_image1.cdn.test.com.cn.log.gz
- fileSize: 10509
- 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
- - dateFrom: 2018-09-05-2300
- dateTo: 2018-09-05-2359
- fileMd5: error
- fileName: 2018-09-05-2300-2330_image1.cdn.test.com.cn.log.gz
- fileSize: 5637
- 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的用法,此处不再赘述,如果你忘记了它的用法,请回顾前文,此处直接写出示例,如下: - tasks:
- - include_vars:
- file: "/testdir/ansible/wsCdnLogList"
- name: testvar
- - debug:
- msg: "{{ item.1.logUrl }}"
- with_subelements:
- - "{{testvar.logs}}"
- - files
复制代码其实,除了使用with_subelements,我们还有另外一种方法,就是使用过滤器,有一个名为json_query的过滤器,可以帮助我们解决上述问题。
我们先来看几个关于json_query的小示例,等熟悉了json_query过滤器以后,再回过头来通过json_query解决上面的问题,小示例如下 假设我们现在有一段简单的json数据,如下: - {
- "users": [
- {
- "name": "tom",
- "age": 18
- },
- {
- "name": "jerry",
- "age": 20
- }
- ]
- }
复制代码上述json数据的yaml格式如下: - ---
- users:
- - name: tom
- age: 18
- - name: jerry
- age: 20
复制代码从上述示例可以看出,一共有两个用户,两个用户的名字分别为tom和jerry,年龄分别为18和20,如果我们想要通过json_query过滤器获取到上述json中的所有user的name,则可以使用如下方法: - ---
- - hosts: test70
- remote_user: root
- gather_facts: no
- tasks:
- - include_vars:
- file: "/testdir/ansible/testvarfile"
- name: testvar
- - debug:
- msg: "{{ testvar | json_query('users[*].name') }}"
复制代码上例的"/testdir/ansible/testvarfile"文件就是上述示例数据所在的文件,它位于ansible主机中,我们将这段数据当做变量赋值给了testvar变量,之后,使用json_query过滤器对这个变量进行了处理,json_query('users.name')表示找到users列表中所有元素的name属性,执行上例playbook后,debug模块的输出信息如下: - TASK [debug] *****************************************
- ok: [test70] => {
- "msg": [
- "tom",
- "jerry"
- ]
- }
复制代码如我们所愿,我们找到了所有用户的name属性,它们被放在了一个列表中展示了出来,你一定已经会举一反三了,查找user列表中的所有元素的age属性的值,则可以使用如下方法 - - debug:
- msg: "{{ testvar | json_query('users[*].age') }}"
复制代码我们再来看一段数据,这段数据位于ansible主机的/testdir/ansible/testvarfile1文件中,此处不再将如下yaml数据转换成json格式,在如下示例中,两种格式没有任何区别,示例数据如下 - ---
- test:
- users:
- - name: tom
- age: 18
- hobby:
- - Skateboard
- - VideoGame
- - name: jerry
- age: 20
- hobby:
- - Music
复制代码从上述示例可以看出,一共有两个用户,除了姓名和年龄属性,每个用户还有爱好属性,爱好属性是一个列表,如果,我们想要获取到所有的爱好,改怎样使用json_query来完成呢?示例如下: - ---
- - hosts: test70
- remote_user: root
- gather_facts: no
- tasks:
- - include_vars:
- file: "/testdir/ansible/testvarfile1"
- name: testvar
- - debug:
- msg: "{{ testvar | json_query('test.users[*].hobby[*]') }}"
复制代码没错,上例表示,获取到users列表中所有hobby列表的所有项,正如你所看到的,当数据结构中存在列表时,我们可以使用"列表名"获取到列表下面的所有项,那么,我们来执行一下上例的playbook,执行后debug模块输出结果如下: - TASK [debug] *************************************
- ok: [test70] => {
- "msg": [
- [
- "Skateboard",
- "VideoGame"
- ],
- [
- "Music"
- ]
- ]
- }
复制代码返回给我们的数据是一个json列表,这个列表中嵌套了两个列表,正是上述示例数据中两个用户的"爱好列表",如果你想要将上例嵌套的列表拉平后输出,则可以使用之前总结的循环进行操作,此处不再赘述。
在上述示例中,我们都是获取到users列表中的所有用户的信息,但是如果我们想要根据条件获取到某个用户的某些信息,该怎么办呢?比如,我想要获取到tom的爱好,则可以使用如下方法: - tasks:
- - include_vars:
- file: "/testdir/ansible/testvarfile1"
- name: testvar
- - debug:
- msg: "{{ testvar | json_query('test.users[?name==`tom`].hobby[*]') }}"
复制代码上例中,json_query('test.users[?name==`tom`].hobby')表示只查找users列表中name属性等于tom的hobby信息,你肯定发现了,上例中,我们使用了"反引号"将tom引起来了,这是因为当你在添加查询条件时,对需要为对应的值添加引号,但是,上例中msg的值的外侧已经存在一对双引号,双引号内又有一对单引号,所以,为了不让引号之间互相冲突,此处使用反引号将tom引起,除了使用反引号,也可以如下方法,实现相同的效果: - tasks:
- - include_vars:
- file: "/testdir/ansible/testvarfile1"
- name: testvar
- - debug:
- msg: "{{ testvar | json_query(querystring) }}"
- vars:
- querystring: "test.users[?name=='tom'].age"
复制代码如上例所示,我们在debug任务中使用vars关键字定义了一个只有当前debug任务能够使用的变量,从而避免了多层引号嵌套时所产生的冲突问题。
我们也可以同时获取到用户的姓名、年龄两个属性的值,当需要同时获取多个属性值时,需要通过键值对的方式调用属性,示例如下: - tasks:
- - include_vars:
- file: "/testdir/ansible/testvarfile1"
- name: testvar
- - debug:
- 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模块输出信息如下: - TASK [debug] **************************************
- ok: [test70] => {
- "msg": [
- {
- "uage": 18,
- "uname": "tom"
- },
- {
- "uage": 20,
- "uname": "jerry"
- }
- ]
- }
复制代码如上所示,用户的姓名和年龄都按照我们定义的方式输出了。
好了,说了这么多,json_query过滤器的用法你肯定已经掌握了,所以,我们回过头来,使用json_query来解决一下文章最开始的问题,我们的目标是,使用json_query过滤器找到cdn厂商返回的json数据中的所有日志的logUrl,那么我们可以怎么编写playbook呢?示例如下 : - ---
- - hosts: test70
- remote_user: root
- gather_facts: no
- vars_files:
- - /testdir/ansible/wsCdnLogList
- tasks:
- - debug:
- msg: "{{item}}"
- with_items: "{{ logs | json_query('[*].files[*].logUrl') }}"
复制代码上例playbook的执行效果我就不粘贴了,快动手试试吧。
希望这篇文章能够帮助到你,加油~
|