RHCE9学习指南 第30章 变量的使用

为了能够写出更实用的playbook,我们需要在playbook中使用变量。下面我们来讲解下在playbook中常见到的变量。本章的实验均在/home/lduan/demo2中创建,先把此目录创建出来并把ansible.cfg和hosts拷贝进去,命令如下。

[lduan@server ~]$ mkdir demo2
[lduan@server ~]$ cp ansible.cfg hosts demo2
[lduan@server ~]$ cd demo2
[lduan@server demo2]$

30.1 手动定义变量

通过vars来定义变量,vars和tasks对齐。定义的格式如下。

vars:
  变量1: 值1
  变量2: 值2
  ....

定义变量时不可有重复的变量,否则后定义的值会覆盖前面定义变量的值,如下所示。

vars:
  aa: value1
  bb: value2
  aa: value3
  ....

这里aa重复定义了,则aa的值最终是value3。
引用变量时用{{变量名}},大括号内侧两边是不是有空格是无所谓的,如下所示。

{{变量名}}
{{  变量名  }}
{{  变量名}}
{{变量名  }}

练习1:
写一个名称为1.yaml的playbook,里面定义3个变量:

    myname1=tom
    myname2=tom2
    myname3=tom3

然后打印myname1的值,命令如下。

[lduan@server demo2]$ cat 1.yaml 
---
- hosts: server2
  vars:
    myname1: tom1
    myname2: tom2
    myname3: tom3
  tasks:
  - name: 打印某个变量
    debug: msg="变量myname1的值是 {{myname1}}"
[lduan@server demo2]$

执行此playbook,命令如下。

[lduan@server demo2]$ ansible-navigator run ./1.yaml -m stdout
    ...输出...
TASK [打印某个变量] 
ok: [server2] => {
    "msg": "变量myname1的值是 tom1"
}
    ...输出...
[lduan@server demo2]$

此处可以看到,这里打印了myname1的值为tom1。
定义变量时,同一个变量定义多次,后面定义的生效,修改1.yaml的内容如下。

[lduan@server demo2]$ cat 1.yaml
---
- hosts: server2
  vars:
    myname1: tom1
    myname2: tom2
    myname3: tom3
    myname1: tom3
  tasks:
  - name: 打印某个变量
    debug: msg='变量myname1的值是 {{myname1}}'
[lduan@server demo2]$

这里定义了两次myname1这个变量,第一次定义的值为tom1,第二次定义的值为tom3,然后执行此playbook看结果,如下所示。

[lduan@server demo2]$ ansible-navigator run ./1.yaml -m stdout
 [WARNING]: While constructing a mapping from /home/tom/rh294/demo2/1.yaml, line 4, column 5, found a duplicate dict key (myname1). Using last defined value only.
    ...输出...
TASK [打印某个变量] 
ok: [server2] => {
    "msg": "变量myname1的值是 tom3"
}
[lduan@server demo2]$

因为myname1重复定义了2次,所以执行此playbook时会有提示,意思是变量重复定义了,且后面定义的myname1生效,打印的结果为tom3。

30.2 变量文件

如果定义的变量太多,可以把变量拿出来单独放在一个文件中,然后在playbook中通过vars_files引用此变量文件,那么就可以直接使用此变量文件中的变量了,如同是变量文件的变量直接在yaml文件中定义的似的,这样更方便管理。
例如,创建文件vars.yaml,内容如下。

[lduan@server demo2]$ cat vars.yaml 
myv1: aaa
myv2: bbb
myv3: ccc
[lduan@server demo2]$

修改1.yaml的内容如下。

[lduan@server demo2]$ cat 1.yaml 
---
- hosts: server2
  vars_files:
  - vars.yaml
  vars:
    myname1: tom1
    myname2: tom2
    myname3: tom3
  tasks:
  - name: 打印某个变量
    debug: msg='变量myname1的值是 {{myname1}}'
  - name: 打印变量myv1的值
    debug: msg='变量myv1的值是 {{myv1}}'
[lduan@server demo2]$

这里通过vars_files来引用变量文件vars.yaml,然后打印变量myv1值,运行结果如下。

[lduan@server demo2]$ ansible-navigator run ./1.yaml -m stdout
    ...输出...
TASK [打印某个变量] 
ok: [server2] => {
    "msg": "变量myname1的值是 tom1"
}
TASK [打印变量myv1的值] 
ok: [server2] => {
    "msg": "变量myv1的值是 aaa"
}
[lduan@server demo2]$

30.3 字典变量

所谓字典(dictionaries简写为dict),就是存储多个变量的容器,可以把字典理解为是一个木桶,里面存放了很多个变量。如图30-1所示,两个木桶xx和yy,里面分别存储了好3个变量:aa1=1, bb=2, cc=3。
file
图30-1 存储多个变量的容器
同一个字典中定义的多个变量不可有重复值,否则后面定义的变量会覆盖前面定义的变量。
要是引用木桶中变量,必须要指定是哪个木桶中的变量,例如,要引用木桶xx中的变量aa,则使用 xx.aa。
定义字典是在vars下定义的,语法如下。

字典名:
  var1: value1
  var2: value2
   ....

这里的字典就如同上图中的桶,里面的var1、var2如同上图中的变量aa、bb等。
注意:在字典中定义这一个变量时,变量前面是不加“-”的,且定义变量没有先后顺序。
通过“字典名.变量名”这种格式来引用变量,看下面的例子。

[lduan@server demo2]$ cat 2.yaml 
---
- hosts: server2
  vars:
    dict1:
      myv1: aaa
      myv2: bbb
      myv3: ccc
    dict2:
      myv1: 111
      myv2: 222
      myv3: 333
  tasks:
  - name: 打印第一个变量
    debug: msg="{{dict1.myv1}}"
[lduan@server demo2]$ 

这里定义了两个个字典dict1和dict2,里面分别有3个变量,最后通过dict1.myv1引用了字典dick1中的变量myv1,通过ansible-playbook 2.yaml执行的结果应该为aaa,如下所示。

[lduan@server demo2]$ ansible-navigator run ./2.yaml -m stdout
    ...输出...
TASK [打印第一个变量] 
ok: [server2] => {
    "msg": "aaa"
}

[lduan@server demo2]$

如果在同一个字典中出现了相同的变量名,则后面定义的变量的值 会覆盖前面定义的变量的值。

30.4 列表变量

列表变量和字典比较容易弄混。这样一个问题,定义一个员工表,里面有3个员工。每个员工通过3个属性记录。

(1)uname:记录用户名。
(2)age:记录年龄。
(3)sex:表示性别。

这个员工表的内容如下。

employee:
  uname: lisi
  age:22
  sex: man

  uname: wangwu
  age: 24
  sex: man

  uname: xiaohua
  age: 21
  sex: wuman

为了看的清楚,这里把每个用户用空白行隔开了。每个用户都是由3个变量组成的一个整体,这个整体是员工表employee的一个元素。所以,这个员工表有3个元素即3个人。
每个元素都有uname,所以列表employee共有3个uname,但是这3个uname不应该是冲突的,因为它们分别属于不同人。
但是为了防止误会,所以每个元素的第一个变量前都加上一个“-”,表示它是这个元素的第一个变量。所以,员工表改成如下所示内容。

employee:
- uname: lisi
  age:22
  sex: man

- uname: wangwu
  age: 24
  sex: man

- uname: xiaohua
  age: 21
  sex: wuman

这样看起来就清晰了很多,这里employee就是一个列表。
那我要是想获取某个元素(即某个员工)的信息该怎么获取呢?可以通过employee[n]的方式,这里的n叫作下标,下标从0开始。
例如,我要获取第一个用户的信息,则通过employee[0]即可,employee[0]得到的值如下所示。

  uname: lisi
  age:22
  sex: man

可以看到,这是由3个变量组成的,这3个变量组成了一个字典即employee[0]就是一个字典。如果想获取第一个用户的用户名,则可以通过employee[0].uname获得。
所以,员工表employee是由3个字典组成:employee[0]、employee[1]、employee[2]。
列表和字典的不同如下所示。

(1)列表的每个元素的首个变量都是以“-”开头。
(2)字典的每个变量前面都不带“-”。

练习1:
判断下面aa1和aa2是列表还是字典

aa1:
- xx: v1
- yy: v2    aa2:
  xx: v1
  yy: v2

这里左边的aa1是列表,每个元素中仅有一个变量,右边的aa2是字典。

练习2:
写一个名称为3-list.yaml的playbook,定义一个用户列表,里面包含3个用户,每个用户由uname、sex、age组成,命令如下。

[lduan@server demo2]$ cat 3-list.yaml 
---
- hosts: server2
  vars:
    users:
    - uname: tom
      sex: men
      age: 19
    - uname: bob
      sex: men
      age: 20
    - uname: mary
      sex: women
      age: 22
  tasks:
  - name: 打印一个变量
    debug: msg={{ users[2] }}
[lduan@server demo2]$

这里列表users定义了3个元素,现在要获取第3个元素用users[2]来表示,运行结果如下。

[lduan@server demo2]$ ansible-navigator run ./3-list.yaml -m stdout
    ...输出...
TASK [打印一个变量] 
ok: [server2] => {
    "msg": {
        "age": 22,
        "sex": "woman",
        "uname": "mary"
    } 
    ...输出..
[lduan@server demo2]$

如果要获取第三个用户的用户名,修改yaml文件如下。

[lduan@server demo2]$ cat 3-list.yaml
---
- hosts: server2
  vars:
    users:
        ...和原来相同...
  tasks:
  - name: 打印一个变量
    debug: msg={{ users[2].uname }}
[lduan@server demo2]$

执行playbook,命令如下。

[lduan@server demo2]$ ansible-navigator run ./3-list.yaml -m stdout
    ...输出...
TASK [打印一个变量] 
ok: [server2] => {
    "msg": "mary"
}
    ...输出...
[lduan@server demo2]$

定义列表时,也可以直接写值不写变量,通过如下方式定义。

listname:
- var1
- var2
- var3
...

这种定义变量的方式可以换成如下内容。

listname: [var1,var2,var3,...]

30.5 数字变量的运算

在yaml文件中如果定义的变量,它的值如果是数字,可以对其数值进行加数学运算的。常见的数学运算符包括:+(加)、-(减)、(乘)、/(除)、 (次方)。
练习:
写一个名称为4-vars.yaml的playbook,定义了变量aa,值为3,然后求aa
2和aa的3次方,命令如下。

[lduan@server demo2]$ cat 4-vars.yaml 
---
- hosts: server2
  vars:
    aa: 3
  tasks:
  - name: 3乘以2的值
    debug: msg={{aa*2}}
  - name: 3的3次方
    debug: msg="{{3**3}}"
[lduan@server demo2]$ 

运行结果如下。

[lduan@server demo2]$ ansible-navigator run ./4-vars.yaml -m stdout
    ...输出...
TASK [3乘以2的值] 
ok: [server2] => {
    "msg": "6"
}
TASK [3的3次方] 
ok: [server2] => {
    "msg": "27"
}
    ...输出...
[lduan@server demo2]$

这里看到aa的值为3,3乘以2的值为6,3的三次方是27。

30.6 注册变量

在playbook中用shell模块执行某个系统命令后,在结果中是不会显示这个命令结果的,这个和在命令行中用ansible命令调用shell模块不一样。
练习:
写一个playbook,里面执行系统命令hostname,命令如下。

[lduan@server demo2]$ cat 5-reg1.yaml 
---
- hosts: server2
  tasks: 
  - name: 执行一个操作系统命令
    shell: 'hostname'
[lduan@server demo2]$

然后执行这个playbook,命令如下。

[lduan@server demo2]$ ansible-navigator run ./5-reg1.yaml -m stdout
    ...输出...
TASK [执行一个操作系统命令] 
changed: [server2]
    ...输出...
[lduan@server demo2]$

可以看到,没有任何的结果输出。如果想查看这个shell命令结果,可以把shell命令的结果保存在一个变量中,这个变量就是注册变量。然后打印这个变量的值即可。修改5-reg1.yaml的内容如下。

[lduan@server demo2]$ cat 5-reg1.yaml 
---
- hosts: server2
  tasks: 
  - name: 执行一个操作系统命令
    shell: 'hostname'
    register: aa
  - name: 打印注册变量aa的值
    debug: msg={{aa}}
[lduan@server demo2]$

执行这个playbook,命令如下。

[lduan@server demo2]$ ansible-navigator run ./5-reg1.yaml -m stdout
    ...输出...
TASK [执行一个操作系统命令] 
TASK [打印注册变量aa的值] 
ok: [server2] => {
    "msg": {
        "changed": true,
        "cmd": "hostname",
        ...输出...
        "rc": 0,
        ...输出...
        "stdout": "server2.rhce.cc",
        "stdout_lines": [
            "server2.rhce.cc"
        ]
}
[lduan@server demo2]$ 

结果中msg后面的内容就是aa的值,可以看到aa就是一个字典。其中cmd是执行的系统命令,rc是此命令的返回值,stdout表示此命令的结果。
所以,如果只获取这个命令的结果,只要打印字典aa中stdout变量即可。修改5-reg1.yaml的内容如下所示。

[lduan@server demo2]$ cat 5-reg1.yaml
---
- hosts: server2
  tasks: 
  - name: 执行一个操作系统命令
    shell: 'hostname'
    register: aa
  - name: 打印注册变量aa的值
    debug: msg={{aa.stdout}}
[lduan@server demo2]$

执行此playbook,命令如下。

[lduan@server demo2]$ ansible-navigator run ./5-reg1.yaml -m stdout
    ...输出...
TASK [执行一个操作系统命令] 
TASK [打印注册变量aa的值] 
ok: [server2] => {
    "msg": "server2.rhce.cc"
}
[lduan@server demo2]$

这里正确的显示了hostname的值为server2.rhce.cc。

30.7 facts变量

ansible是通过setup模块是可以获取到被管理主机的所有信息的,这些信息都是以变量的方式存在,这些变量称之为facts,前面在setup变量中已经介绍过了,现在写一个名称为6-fact.yaml的playbook用于打印server2的IP和server2的主机名,代码如下。

[lduan@server demo2]$ cat 6-fact.yaml
---
- hosts: server2
  vars:
    list1: ['aaa: ']
  tasks:
  - name: 打印IP
    debug: msg={{ansible_default_ipv4.address}}
  - name: 打印主机名
    debug: msg={{ansible_fqdn}}
[lduan@server demo2]$

打印IP能用到的fact变量是ansible_default_ipv4.address,获取主机名的fact变量是ansible_fqdn。

注意:这里ansible_default_ipv4.address可以写成
ansible_facts.default_ipv4.address 。

打印执行结果如下。

[lduan@server demo2]$ ansible-navigator run ./6-fact.yaml -m stdout
    ...输出...
ok: [server2] => {
    "msg": "192.168.26.102"
}
ok: [server2] => {
    "msg": "server2.rhce.cc"
}
    ...输出...
[lduan@server demo2]$

这里显示server2的IP是192.168.26.102。

30.8 内置变量groups

在ansible中除用户手动的去定义一些变量外,还有一些内置的变量,这些变量不需要用户实现定义就可以直接使用。
groups用于列出清单文件中所有定义的主机组及里面的主机,看下面的例子。

[lduan@server demo2]$ cat 7-group.yaml 
---
- hosts: server2
  tasks:
  - name: xxx
    debug: msg={{groups}}
[lduan@server demo2]$

运行此playbook,命令如下。

[lduan@server demo2]$ ansible-navigator run ./7-group.yaml -m stdout
    ...输出...
ok: [server2] => {
    "msg": {
        "all": [
            "server2",
            "server3"
        ],
        "db": [
            "server2",
            "server3"
        ],
        "ungrouped": []
    }
}
    ...输出...
[lduan@server demo2]$

这里显示了清单文件中所有的主机组及里面的主机信息。
如果想只列出某个主机组可以使用

groups.主机组名   或    groups['主机组名']

来表示,把7-group.yaml改成如下内容。

[lduan@server demo2]$ cat 7-group.yaml
---
- hosts: server2
  tasks:
  - name: xxx
    debug: msg={{ groups['db'] }}
[lduan@server demo2]$

这里只显示主机组aa中的主机,运行结果如下。

[lduan@server demo2]$ ansible-navigator run ./7-group.yaml -m stdout
    ...输出...
TASK [xxx] 
ok: [server2] => {
    "msg": [
        "server2",
        "server3"
    ]
}
    ...输出...
[lduan@server demo2]$

可以看到,这里只显示db主机组中的主机。
这里groups['db']可以改写成groups.db,代码如下。

[lduan@server demo2]$ cat 7-group.yaml
---
- hosts: server2
  tasks:
  - name: xxx
    debug: msg={{ groups.db }}
[lduan@server demo2]$

30.9 内置变量hostvars

hostvars用来显示指定主机的fact变量,用法如下。

hostvars[‘主机名’].键值

此变量一般用于,当某个play的hosts中只写了A主机组,但是同时想在此play中显示B主机组中的信息,这时可以选择此变量。
练习:
写一个playbook里面包含一个play,里面的hosts指定为server2,但是要显示server3的IP地址,命令如下。

[lduan@server demo2]$ cat 8-hostvars.yaml
---
- hosts: server2
  tasks:
  - name: 打印server3的IP
    debug: msg={{hostvars['server3'].ansible_default_ipv4.address}}
[lduan@server demo2]$

运行这个playbook,命令如下。

[lduan@server demo2]$ ansible-navigator run ./8-hostvars.yaml -m stdout
    ...输出...
fatal: [server2]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'ansible.vars.hostvars.HostVarsVars object' has no attribute 'ansible_default_ipv4'\n\nThe error appears to be in '/home/tom/rh294/demo2/8-hostvars.yaml': line 4, column 5, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n  tasks:\n  - name: 打印server3的IP\n    ^ here\n"}
    ...输出...
[lduan@server demo2]$

这里却出现了报错,这是因为play的hosts中只写了server2,所以只会获取server2的fact变量,并不会获取server3的fact变量,就不会识别server3上的ansible_default_ipv4.address。
修改8-hostvars.yaml的值如下所示。

[lduan@server demo2]$ cat 8-hostvars.yaml 
---
- hosts: server3

- hosts: server2
  tasks:
  - name: 打印server3的IP
    debug: msg={{hostvars['server3'].ansible_default_ipv4.address}}
[lduan@server demo2]$ 

这里只比前面多了一个play,且这个play中只写了一个 hosts: server3。但是就是这一句就可以获取到server3的fact变量,这样在第二个play中再次获取server3的fact变量时就不会报错了。执行结果如下。

[lduan@server demo2]$ ansible-navigator run ./8-hostvars.yaml -m stdout
    ...输出...
TASK [Gathering Facts] 
ok: [server3]
TASK [Gathering Facts] 
ok: [server2]

TASK [打印server3的IP] 
ok: [server2] => {
    "msg": "192.168.26.103"
}

[lduan@server demo2]$

可以看到,此处已经正确的获取到了server3的IP了。

30.10 内置变量inventory_hostname

当在ansible主机同时在多台被管主机上执行任务时,每台被管主机都会有一个变量记录它在清单文件中名称是什么,如图30-2所示。
file
图30-2 主机记录清单名称
这个变量就是inventory_hostname,记录了每个主机在清单文件中的名称。
练习:
写一个playbook,在主机组db上运行,命令如下。

[lduan@server demo2]$ cat 9-inventory1.yaml 
---
- hosts: db
  tasks:
  - name: 打印我在清单文件中的名称
    debug: msg={{inventory_hostname}}
[lduan@server demo2]$

这里playbook会在db主机组上执行,即在server2和server3上执行。在server2上执行时inventory_hostname的值为server2, 在server3上执行时inventory_hostname的值为server3,执行的结果如下。

[lduan@server demo2]$ ansible-navigator run ./9-inventory1.yaml -m stdout
    ...输出...
TASK [打印我在清单文件中的名称] 
ok: [server2] => {
    "msg": "server2"
}
ok: [server3] => {
    "msg": "server3"
}
    ...输出...
[lduan@server demo2]$

上面的例子中,hosts的值写的是db,所以后续的task是要在server2和server3上同时执行的。修改清单文件hosts,添加一个主机组xx,里面的主机包括server2这一台主机。

[lduan@server demo2]$ cat hosts
server2
server3
[db]
server2
server3
[xx]
server2
[lduan@server demo2]$

修改playbook文件9-inventory1.yaml内容如下。

[lduan@server demo2]$ cat 9-inventory1.yaml 
---
- hosts: db
  tasks:
  - name: 打印我在清单文件中的名称
    debug: msg={{inventory_hostname}}
    when: inventory_hostname in groups ['xx'] 
[lduan@server demo2]$

这里增加了一条判断语句when(后面会专门讲解),执行debug模块的条件是被管理主机要属于xx主机组才行。虽然hosts后面跟的是db,server3在db组但没有在xx主机组中,条件不满足,所以在server3上并不执行debug模块,执行此模块,命令如下。

[lduan@server demo2]$ ansible-navigator run ./9-inventory1.yaml -m stdout
    ...输出...
ok: [server2]
ok: [server3]

TASK [打印我在清单文件中的名称] 
ok: [server2] => {
    "msg": "server2"
}
skipping: [server3]
    ...输出...
[lduan@server demo2]$

这里跳过了server3,只有server2才执行了debug模块。

30.11 变量的过滤器

所谓变量过滤器实际上就是对变量的值进行一些操作,例如,进行类型转化、截取、加密等操作。使用格式如下。

{{ 变量名 | 函数 }}

练习:
把大写字符转变为小写字符,命令如下。

[lduan@server demo2]$ cat 10-vars1.yaml 
---
- hosts: server2
  vars:
    aa: tom
    bb: BOB
  tasks:
  - name: xxx
   debug: msg={{bb | lower}}

这里定义了一个变量bb值为大小的BOB,通过lower这个过滤器会把大小字符转变为小写字符,运行此playbook,命令如下。

[lduan@server demo2]$ ansible-navigator run ./10-vars1.yaml -m stdout
    ...输出...
TASK [xxx] 
ok: [server2] => {
    "msg": "bob"
} 
    ...输出...
[lduan@server demo2]$

可以看到,这里显示的为小写的bob。
下面列出几个常见的过滤器。

30.11.1 数字类型

整形int,可以把字符串转变为整形,看下面的例子。

[lduan@server demo2]$ cat 10-vars2.yaml 
---
- hosts: server2
  tasks:
  - name: 数学运算
    debug: msg={{3+'3'}}
[lduan@server demo2]$ 

这里对3+’3’进行数学运行,但是第二个3用单引号引起来了说明是一个字符串,用数字3和字符串相加是要报错的,运行结果如下。

[lduan@server demo2]$ ansible-navigator run ./10-vars2.yaml -m stdout
    ...输出...
TASK [数学运算] 
fatal: [server2]: FAILED! => {"msg": "Unexpected templating type error occurred on ({{3+'3'}}): unsupported operand type(s) for +: 'int' and 'str'"}
    ...输出...
[lduan@server demo2]$

这里报错了提示信息意思是,数字不能和字符串进行数学运算。我们可以把字符串’3’ 通过int转成数字,把playbook文件内容改成如下。

[lduan@server demo2]$ cat 10-vars2.yaml
---
- hosts: server2
  tasks:
  - name: 数学运算
    debug: msg={{3+('3'|int)}}
[lduan@server demo2]$

其中’3’通过管道传递给int,转变为整型类型的,这样就可以相加了,运行结果如下。

[lduan@server demo2]$ ansible-navigator run ./10-vars2.yaml -m stdout
    ...输出...
TASK [数学运算] 
ok: [server2] => {
    "msg": "6"
}
    ...输出...
[lduan@server demo2]$

浮点型float,可以把字符串转换成小数类型的数字,修改10-vars2.yaml的内容如下。

[lduan@server demo2]$ cat 10-vars2.yaml
---
- hosts: server2
  tasks:
  - name: 数学运算
    debug: msg={{3+('3'|float)}}
[lduan@server demo2]$ 

这里用float把’3’转变为浮点型,即3.0,运行结果如下。

[lduan@server demo2]$ ansible-navigator run ./10-vars2.yaml -m stdout
    ...输出...
TASK [数学运算] 
ok: [server2] => {
    "msg": "6.0"
}
    ...输出...
[lduan@server demo2]$

绝对值abs,可以把负数转换成正数,如-3变成3,修改10-vars2.yaml的内容如下。

[lduan@server demo2]$ cat 10-vars2.yaml
---
- hosts: server2
  tasks:
  - name: 数学运算
    debug: msg={{-3 | abs }}
[lduan@server demo2]$

这里用abs求-3的绝对值,得到的值应该是3,运行的结果如下。

[lduan@server demo2]$ ansible-navigator run ./10-vars2.yaml -m stdout
    ...输出...
TASK [数学运算] 
ok: [server2] => {
    "msg": "3"
}
    ...输出...
[lduan@server demo2]$

30.11.2 列表

前面讲过一个列表中可以包括多个值,列表的过滤符可以求出列表的长度、最小值、最大值等。

(1)length:用于求列表长度。
(2)max:用于求列表中最大值。
(3)min:用于求列表中最小值。

练习:
playbook内容如下所示。

[lduan@server demo2]$ cat 10-vars3.yaml
---
- hosts: server2
  vars:
    list1: [1,2,8,3,2]
  tasks:
  - name: 求列表长度
    debug: msg="{{list1 | length}}"
  - name: 求列表中最大值
    debug: msg="{{list1 | max}}"
  - name: 求列表中最小值
    debug: msg="{{list1 | min}}"
[lduan@server demo2]$

定义一个列表list1,然后分别求列表的长度,最大值,最小值,运行结果如下。

[lduan@server demo2]$ ansible-navigator run ./10-vars3.yaml -m stdout
    ...输出...
TASK [求列表长度] 
ok: [server2] => {
    "msg": "5"
}
TASK [求列表中最大值] 
ok: [server2] => {
    "msg": "8"
}
TASK [求列表中最小值] 
ok: [server2] => {
    "msg": "1"
}
    ...输出...
[lduan@server demo2]$

列表长度为5,说明列表中有5个元素,最大值为8,最小值为1。
用于列表的过滤器还包括sort(排序)、sum(求和)、shuffle(打乱顺序显示)等。

30.11.3 设置变量默认值default

如果某个变量没有定义,那么可以通过default给它设置一个默认值。用法如下。

 {{ var1 | default(value1 ) }}

如果某个变量var1已经定义了,则显示它自己的值,如果没有被定义,则被复制为value1。
练习:
写一个playbook内容如下。

[lduan@server demo2]$ cat 10-vars4.yaml
---
- hosts: server2
  vars:
    aa: 11
    bb:
  tasks:
  - name: aa的值
    debug: msg="{{ aa | default('xxx')}}"
  - name: bb的值
    debug: msg="{{ bb | default('xxx')}}"
  - name: cc的值
    debug: msg="{{ cc | default('xxx')}}"
[lduan@server demo2]$

这里定义了aa的值为11,定义了bb但是没有赋值,并没有定义变量cc。所以,打印aa时,会显示自己的值即11,打印bb时,会显示自己的值即空值,打印cc的值时,显示的是default中的值即xxx。运行结果如下。

[lduan@server demo2]$ ansible-navigator run ./10-vars4.yaml -m stdout
    ...输出...
TASK [aa的值] 
ok: [server2] => {
    "msg": "11"
}
TASK [bb的值] 
ok: [server2] => {
    "msg": ""
}
TASK [cc的值] 
ok: [server2] => {
    "msg": "xxx"
}
    ...输出...
[lduan@server demo2]$

30.11.4 字符串相关

string把其他类型数据类型转变为字符串,看下面的例子。

[lduan@server demo2]$ cat 10-vars5.yaml 
---
- hosts: server2
  tasks:
  - name: 求和
    debug: msg="{{ 3+(3|string)}}"
[lduan@server demo2]$ 

3+3本身是可以正常运行的,但这里通过3|string把第二个3转变为了字符串,因为数字只能和数字相加,所以上述执行数字3和字符串3相加会报错,运行结果如下。

[lduan@server demo2]$ ansible-navigator run ./10-vars5.yaml -m stdout
    ...输出...
TASK [求和] 
fatal: [server2]: FAILED! => {"msg": "Unexpected templating type error occurred on ({{ 3+(3|string)}}): unsupported operand type(s) for +: 'int' and 'str'"}
    ...输出...
[lduan@server demo2]$

这个用法其实和前面的3+’3’是类似的,单引号也可以转变为字符串。
capitalize过滤器用于把字符串的首字符变为大写,看下面的例子。

[lduan@server demo2]$ cat 10-vars6.yaml
---
- hosts: server2
  tasks:
  - name: 字符串转换
    debug: msg="{{ 'aa' | capitalize }}"
[lduan@server demo2]$

运行此playbook,命令如下。

[lduan@server demo2]$ ansible-navigator run ./10-vars6.yaml -m stdout
    ...输出...
TASK [字符串转换] 
ok: [server2] => {
    "msg": "Aa"
}
    ...输出...
[lduan@server demo2]$

可以看到,aa通过capitalize过滤器转变之后,首字符变成了大写。
关于字符的过滤器还有upper(把小写字符变成大写)、lower(把大写字符变成小写),这些请大家自行练习。

30.11.5 加密相关

有时需要对字符串进行加密操作,例如,在创建用户时给用户设置密码,就得以密文的方式不能用明文的密码。
求哈希值hash,算法可以是md5或sha1等,用法如下。

hash(‘算法名’)

练习1:
写一个playbook,实现对字符串haha001实现不同的加密,命令如下。

[lduan@server demo2]$ cat 10-vars7.yaml 
---
- hosts: server2
  vars:
    passa: haha001
  tasks:
  - name: 用md5加密
    debug: msg={{passa | hash('md5')}}
  - name: 用sha1加密
    debug: msg={{passa | hash('sha1')}}
  - name: 用sha512加密
    debug: msg={{passa | hash('sha512')}}
[lduan@server demo2]$

这里定义了一个变量passa=haha001,然后分别使用md5、sha1、sha512对它加密,运行结果如下。

[lduan@server demo2]$ ansible-navigator run ./10-vars7.yaml -m stdout
    ...输出...
TASK [用md5加密] 
ok: [server2] => {
    "msg": "da48dd48779209245c671bf9fddfde23"
}
TASK [用sha1加密] 
ok: [server2] => {
    "msg": "0d7c6d97655f38d7773c9a78a0861d533b1b32f1"
}
TASK [用sha512加密] 
ok: [server2] => {
    "msg": "b37c491d6a36ef90c6c718855c06cc53053fe646fcae8889fafbad198c07a91fc283dd266af5dceb5378ff0a04cc1f9062fd4ea203c81dd2c9fbc58efa72a68b"
}
    ...输出...
[lduan@server demo2]$

注意:这里hash过滤器中的md5或sha1要用单引号引起来。

除使用hash作为过滤器加密外,还可以使用password_hash作为过滤器,用法如下。

password_hash(‘算法名’)

在Linux系统中用户密码一般使用的是sha512加密算法,所以一般用password_hash('sha512')给用户的密码加密。
修改10-vars7.yaml的内容如下。

[lduan@server demo2]$ cat 10-vars7.yaml
---
- hosts: server2
  vars:
    passa: haha001
  tasks:
  - name: password_hash过滤器 sha512
    debug: msg={{passa | password_hash('sha512')}}
[lduan@server demo2]$

这里调用sha512对变量aa的值(就是redhat)进行加密,运行结果如下。

[lduan@server demo2]$ ansible-navigator run ./10-vars7.yaml -m stdout
    ...输出...
TASK [password_hash过滤器 sha512] 
ok: [server2] => {
    "msg": "$6$U.4Jm.BHngn4.rdN$A1t3D.dXG/OZ7xcraL.m4wPPZ3Mu7Z0y2LoMt4r8iHO7w3YzX0jE2bctGYGK3Rhkgb6d2hlG7/d49lkW.wZXK0"
}
    ...输出...
[lduan@server demo2]$

注意:这里password_hash后面小括号中的sha512要用单引号引起来。
练习2:
在server2上创建用户bob,并设置密码为haha001。
先确定server2上不存在bob用户,命令如下。

[lduan@server demo2]$ ansible server2 -m shell -a "id bob"
server2 | FAILED | rc=1 >>
id: “bob”:无此用户non-zero return code

[lduan@server demo2]$

然后开始写playbook文件,内容如下。

[lduan@server demo2]$ cat 10-vars8.yaml
---
- hosts: server2
  vars:
    passa: haha001
  tasks:
  - name: 创建一个bob用户
    user: user=bob comment="Im bob" groups=root password={{passa | password_hash('sha512')}}
[lduan@server demo2]$

这里调用user模块,name指定用户名为bob,comment用于指定用户的描述信息,password参数用于指定密码。
运行结果如下。

[lduan@server demo2]$ ansible-navigator run ./10-vars8.yaml -m stdout
    ...输出...
TASK [创建一个bob用户] 
changed: [server2] 
    ...输出...
[lduan@server demo2]$

验证,切换到server2上,命令如下。

[root@server2 ~]# su - tom
[tom@server2 ~]$ su - bob
密码:
[bob@server2 ~]$

这里先从root切换到tom用户,因为root切换到其他任何用户都不需要密码。从tom用户su到bob用户时需要输入密码“haha001”,证明bob用户已经创建成功且密码也是正确的。

作业

1.写一个名称为chap30-1.yaml的playbook,要求在server2上执行任务,获取server3上的IP地址。

2.写一个名称为chap30-2.yaml的playbook,要求在server2上执行hostname命令,然后打印命令的结果和返回值,以如下格式输出:
命令的结果 返回值

3.写一个名称为chap30-3.yaml的playbook,要求显示db主机组中的主机。

4.写一个名称为chap30-4.yaml的playbook,要求显示主机server2上/dev/sda1的大小。

相关新闻

发表回复

Please Login to Comment

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理

                                                                                                                                    RHCE9学习指南全部更新完成,点击阅读