列表和字典

列表

[]

使用"- "(减号加一个或多个空格)作为列表项

1. - zhangsan

2.  - lisi

3.  - wangwu

字典

{}

一般表示为

key: value

调用模块,动作,实体文件一般为列表;

 

标签tag

可以为playbook中的每个任务都打上标签,标签的主要作用是可以在ansible-playbook中设置只执行哪些被打上tag的任务或忽略被打上tag的任务。

tasks: 
    - name: make sure apache is running
      service: name=httpd state=started
      tags: apache
    - name: make sure mysql is running
      service: name=mysqld state=started
      tags: mysql

以下是ansible-playbook命令关于tag的选项。

--list-tags           # list all available tags
-t TAGS, --tags=TAGS  # only run plays and tasks tagged with these values
--skip-tags=SKIP_TAGS # only run plays and tasks whose tags do not match these values

变量

1、ansible facts

facts组件是用来收集被管理节点信息的,使用setup模块可以获取这些信息。

ansible web -m setup 收集web节点信息
ansible web -m setup -a "filter=*ipv4" 用filter可以筛选指定的facts信息,支持通配符
web1 | SUCCESS => {
    "ansible_facts": {
        "ansible_default_ipv4": {
            "address": "192.168.2.4", 
            "alias": "ens33", 
            "broadcast": "192.168.2.255", 
            "gateway": "192.168.2.2", 
            "interface": "ens33", 
            "macaddress": "00:0c:29:9e:d2:6e", 
            "mtu": 1500, 
            "netmask": "255.255.255.0", 
            "network": "192.168.2.0", 
            "type": "ether"
        }, 
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false
}

2、变量引用json数据的方式

在ansible中,任何一个模块都会返回json格式的数据,即使是错误信息都是json格式的。

在ansible中,json格式的数据,其内每一项都可以通过变量来引用它。当然,引用的前提是先将其注册为变量。(使用register模块注册)

例如,下面的playbook是将shell模块中echo命令的结果注册为变量,并使用debug模块输出

---
    - hosts: 192.168.100.65
      tasks:
        - shell: echo hello world
          register: say_hi
        - debug: var=say_hi

debug输出结果

TASK [debug] *********************************************************************
ok: [web1] => {
    "say_hi": {
        "changed": true, 
        "cmd": "echo hello world", 
        "delta": "0:00:00.003011", 
        "end": "2020-02-28 13:17:46.464762", 
        "failed": false, 
        "rc": 0, 
        "start": "2020-02-28 13:17:46.461751", 
        "stderr": "", 
        "stderr_lines": [], 
        "stdout": "hello world", 
        "stdout_lines": [
            "hello world"
        ]
    }
}

可以看出,结果是一段json格式的数据,最顶端的key为say_hi,其内是一大段的字典(即使用大括号包围的),其中的stdout_lines还包含了一个json数组,也就是所谓的yaml列表项(即使用中括号包围的)。

---
      - hosts: web1
        tasks:
         - shell: |
                  echo hello world
                  echo hi san
           register: say_hi
         - debug: var=say_hi.stdout_lines[0]
         - debug: var=say_hi.stdout_lines[1]

TASK [debug] **************************************************************
ok: [web1] => {
    "say_hi.stdout_lines[0]": "hello world"
}

TASK [debug] **************************************************************

ok: [web1] => {
    "say_hi.stdout_lines[1]": "hi san"
}

3、引用json字典数据的方式

如果想要输出json数据的某一字典项,则应该使用"key.dict"或"key['dict']"的方式引用。

例如最常见的stdout项"hello world"是想要输出的项,以下两种方式都能引用该字典变量。

- debug: var=say_hi.stdout
- debug: var=sya_hi['stdout']

或"key['dict']"的方式都能引用,但在dict字符串本身就包含"."的时候,应该使用中括号的方式引用。例如:

anykey['192.168.2.3']

4、引用json数组数据的方式

如果想要输出json数据中的某一数组项(列表项),则应该使用"key[N]"的方式引用数组中的第N项,其中N是数组的index,从0开始计算。如果不使用index,则输出的是整个数组列表。

如上例中的

- debug: var=say_hi.stdout_lines[0]
 - debug: var=say_hi.stdout_lines[1]

5、引用facts数据

显然,facts数据的顶级key为ansible_facts,在引用时应该将其包含在变量表达式中。但自动收集的facts比较特殊,它以ansible_facts作为key,ansible每次收集后会自动将其注册为变量,所以facts中的数据都可以直接通过变量引用,但连顶级key ansible_facts必须要省略。

例如引用上面的ipv4的地址address项。

ansible_eth0.ipv4.address

而不能写成:

ansible_facts.ansible_eth0.ipv4.address

在playbook中引用:{{ansible_eth0.ipv4.address}},,变量引用要加{{}}

6、设置本地facts

在ansible收集facts时,还会自动收集/etc/ansible/facts.d/*.fact文件内的数据到facts中,且以ansible_local做为key。目前fact支持两种类型的文件:ini和json。当然,如果fact文件的json或ini格式写错了导致无法解析,那么肯定也无法收集。

例如,在/etc/ansible/facts.d目录下存在一个my.fact的文件,其内数据如下:

   shell> cat /etc/ansible/facts.d/my.fact

{
     "family": {
         "father": {
             "name": "Zhangsan",
             "age": "39"
         },
         "mother": {
             "name": "Lisi",
                       "age": "35"
                   }
               }
           }

ansible收集facts后的本地facts数据如下:

"filter=ansible_local"

localhost | SUCCESS => {
     "ansible_facts": {
        "ansible_local": {
            "my": {
                 "family": {
                     "father": {
                        "age": "39", 
                        "name": "Zhangsan"
                               }, 
                              "mother": {
                                   "age": "35", 
                                   "name": "Lisi"
                               }
                           }
                       }
                   }
               }, 
               "changed": false
           }

可见,如果想要引用本地文件中的某个key,除了带上ansible_local外,还必须得带上fact文件的文件名。例如,引用father的name。

ansible_local.my.family.father.name

7、输出和引用变量

上文已经展示了一种变量的引用方式:使用debug的var参数。debug的另一个参数msg也能输出变量,且msg可以输出自定义信息,而var参数只能输出变量。

另外,msg和var引用参数的方式有所不同。例如:

---
   - hosts: 192.168.100.65
     tasks:
       - debug: 'msg="ipv4 address: {{ansible_eth0.ipv4.address}}"'
       - debug: var=ansible_eth0.ipv4.address

msg引用变量需要加上双大括号包围,既然加了大括号,为了防止被解析为内联字典,还得加引号包围。这里使用了两段引号,因为其内还包括了一个": ",加引号可以防止它被解析为"key: "的格式。而var参数引用变量则直接指定变量名。

 

注册和定义变量的各种方式

ansible中定义变量的方式有很多种,大致有:(1)将模块的执行结果注册为变量;(2)直接定义字典类型的变量;(3)role中文件内定义变量;(4)命令行传递变量;(5)借助with_items迭代将多个task的结果赋值给一个变量;(6)inventory中的主机或主机组变量;(7)内置变量。

1、register注册变量

: say_hi

2、set_fact定义变量

类似shell中变量的赋值方式,可以将某个变量的值赋值给另一个变量,也可以将字符串赋值给变量。

- shell: echo haha
       register: say_hi
    - set_fact: var1="{{say_hi.stdout}}"
    - set_fact: var2="your name is"
    - debug: msg="{{var2}} {{var1}}"

3、 vars定义变量

可以在play或task层次使用vars定义字典型变量。如果同名,则task层次的变量覆盖play层次的变量。

一个play包含一个或多个task

---
   - hosts: localhost
     vars:
       var1: value1
       var2: value2
     tasks:
       - debug: msg="{{var1}} {{var2}}"
         vars: 
           var2: value2.2

 

TASK [debug] *********************************************************************
ok: [localhost] => {
    "msg": "value1 value2.2"
}

4、vars_files定义变量

和vars一样,只不过它是将变量以字典格式定义在独立的文件中,且vars_files不能定义在task层次,只能定义在play层次。

---
  - hosts: localhost
     vars_files:
       -/tmp/var_file1.yml
       - var_file2.yml
     tasks:
       - debug: msg="{{var1}} {{var2}}"

上面var_file2.yml使用的是相对路径,基于playbook所在的路径。例如该playbook为/tmp/x.yml,则var_file2.yml也应该在/tmp下。当然,完全可以使用绝对路径。

5、roles中的变量

由于role是整合playbook的,它有默认的文件组织结构。其中有一个目录vars,其内的main.yml用于定义变量。还有defaults目录内的main.yml则是定义role默认变量的,默认变量的优先级最低。

ansible和ansible-playbook命令的"-e"选项都可以传递变量,传递的方式有两种:-e key=value-e @var_file。注意,当key=value方式传递变量时,如果变量中包含特殊字符,必须防止其被shell解析。

例如:

ansible localhost -m shell -a "echo {{say_hi}}"-e 'say_hi="hello world"'
ansible localhost -m shell -a "echo {{say_hi}}"-e @/tmp/var_file1.yml

其中/tmp/var_file1.yml中的内容如下:

---
    say_hi: hello world

7、借助with_items叠加变量

---
   - hosts: localhost
     tasks:
      - shell: echo "{{item}}"
        with_items:
          - haha
          - heihei
        register: var_hi
      - debug: msg="{{var_hi.results[0].stdout}}"
      - debug: msg="{{var_hi.results[1].stdout}}"

8、inventory中主机变量和主机组变量

在inventory文件中可以为主机和主机组定义变量,不仅包括内置变量赋值,还包括自定义变量赋值。

内置变量

ansible除了inventory中内置的一堆不可被引用的设置类变量,还有几个全局都可以引用的内置变量,主要有以下几个:
inventory_hostname、inventory_hostname_short、groups、group_names、hostvars、play_hosts、inventory_dir和ansible_version。

1.inventory_hostname和inventory_hostname_short

分表代表的是inventory中被控节点的主机名和主机名的第一部分,如果定义的是主机别名,则变量的值也是别名。

例如inventory中centos7主机组定义为如下:

[centos7]
192.168.100.63
host1 ansible_ssh_host=192.168.100.64
www.host2.com ansible_ssh_host=192.168.100.65

分别输出它们的inventory_hostnameinventory_hostname_short

shell> ansible centos7 -m debug -a 'msg="{{inventory_hostname}} & {{inventory_hostname_short}}"'
    192.168.100.63| SUCCESS =>{
    "msg":"192.168.100.63 & 192"
    }
    host1 | SUCCESS =>{
    "msg":"host1 & host1"
    }
    www.host2.com | SUCCESS =>{
    "msg":"www.host2.com & www"

2、groups和group_names

group_names返回的是主机所属主机组,如果该主机在多个组中,则返回多个组,如果它不在组中,则返回ungrouped这个特殊组。

ansible web -m debug -a 'msg="{{groups}} {{group_names}}"'
web2 | SUCCESS => {
    "msg": "{'ungrouped': [], u'web': [u'web1', u'web2'], 'all': [u'web1', u'web2']} [u'web']"
}
web1 | SUCCESS => {
    "msg": "{'ungrouped': [], u'web': [u'web1', u'web2'], 'all': [u'web1', u'web2']} [u'web']"
}

3、hostvars

该变量用于引用其他主机上收集的facts中的数据,或者引用其他主机的主机变量、主机组变量。其key为主机名或主机组名。

举个例子,假如使用ansible部署一台php服务器host1,且配置文件内需要指向另一台数据库服务器host2的ip地址ip2,可以直接在配置文件中指定ip2,但也可以在模板配置文件中直接引用host2收集的facts数据中的ansible_eth0.ipv4.address变量。

 

---
   - hosts: centos7
     tasks:
    - debug: msg="{{hostvars['192.168.2.4'].ansible_eth0.ipv4.address}}"

hostvars后面接的key要是/etc/ansible/hosts文件中的主机名(ip,别名)或主机组名

- debug: msg="{{hostvars[inventory_hostname].ansible_ens33.ipv4.address}}"
      - debug: msg="{{inventory_hostname}}"
TASK [debug] ******************************************************
ok: [web1] => {
    "msg": "192.168.2.4"
}
ok: [web2] => {
    "msg": "192.168.2.5"
}

TASK [debug] *******************************************************

ok: [web1] => {
    "msg": "web1"
}
ok: [web2] => {
    "msg": "web2"
}

4、play_hosts和inventory_dir

play_hosts代表的是当前play所涉及inventory内的所有主机名列表。

那么,该inventory内的任意一或多台主机作为ansible或ansible-playbook的被控节点时,都会返回整个inventory内的所有主机名称。

inventory_dir是所使用inventory所在的目录。

[root@localhost ansible]# ansible web -m debug -a 'msg="{{play_hosts}}"'
web2 | SUCCESS => {
    "msg": [
        "web1", 
        "web2"
    ]
}
web1 | SUCCESS => {
    "msg": [
        "web1", 
        "web2"
    ]
}
[root@localhost ansible]# ansible web -m debug -a 'msg="{{play_hosts}} {{inventory_dir}}"'
web1 | SUCCESS => {
    "msg": "[u'web1', u'web2'] /etc/ansible"
}
web2 | SUCCESS => {
    "msg": "[u'web1', u'web2'] /etc/ansible"
}

 5、ansible_version

代表的是ansible软件的版本号。变量返回的内容如下:

[root@localhost ansible]# ansible web1 -m debug -a 'msg="{{ansible_version}}"'
web1 | SUCCESS => {
    "msg": {
        "full": "2.9.3", 
        "major": 2, 
        "minor": 9, 
        "revision": 3, 
        "string": "2.9.3"
    }
}

 

[root@localhost ansible]# ansible web1 -m shell -a 'echo "{{ansible_version}}"'
web1 | CHANGED | rc=0 >>
{'major': 2, 'full': '2.9.3', 'string': '2.9.3', 'minor': 9, 'revision': 3}

循环

1、with_items迭代列表

ansibel支持迭代功能。例如,有一大堆要输出的命令、一大堆要安装的软件包、一大堆要copy的文件等等。

两种写法:

tasks: 
       - yum: name="{{item}}" state=installed
          with_items: 
                - pkg1
                - pkg2
                - pkg3
- command: echo {{ item }}
      with_items: [ 0, 2, 4, 6, 8, 10 ]
      register: num
    - debug: msg="{% for i in num.results %} {{i.stdout}} {% endfor %}"
这是jinj2的语法格式:   {% for i in 数组 %} {{我们要做的事}} {% endfor %}

2、with_dict迭代字典项

使用"with_dict"可以迭代字典项。迭代时,使用"item.key"表示字典的key,"item.value"表示字典的值。

例如:

---
   - hosts: localhost
     tasks:
       - debug: msg="{{item.key}} & {{item.value}}"
          with_dict: { address: 1,netmask: 2,gateway: 3 }

另一种情况,字典是已存储好的。例如ansible facts中的ansible_eth0.ipv4,其内容如下:

"ipv4": {
    "address": "192.168.100.65",
    "netmask": "255.255.255.0",
    "gateway": "192.168.100.2"
}

这种情况下,with_dict处可以直接指定该字典的key。即:

---
     - hosts: localhost
       tasks:
         - debug: msg="{{item.key}} & {{item.value}}"
           with_dict: ansible_eth0.ipv4

3、with_fileglob迭代文件

例如,拷贝一堆用通配符匹配出来的文件到各远程主机上。

---
   - hosts: centos
     tasks: 
       - copy: src="{{item}}" dest=/tmp/
         with_fileglob:
- /tmp/*.sh
            - /tmp/*.py

注意,通配符无法匹配"/",因此无法递归到子目录中,也就无法迭代子目录中的文件。

4、with_lines迭代行

with_lines很好用,可以将命令行的输出结果按行迭代。

例如,find一堆文件出来,copy走。

---
   - hosts: localhost
     tasks:
        - copy: src="{{item}}" dest=/tmp/yaml
          with_lines:
             - find /tmp -type f -name "*.yml"

5、with_nested嵌套迭代

嵌套迭代是指多次迭代列表项。例如:

---
    - hosts: localhost
      tasks:
       - debug: msg="{{item[0]}} & {{item[1]}}"
         with_nested:
            -[a,b]
            -[1,2,3]

结果将得到"a & 1"、"a & 2"、"a & 3"、"b & 1"、"b & 2"和"b & 3"共6个结果。