“RHCE8.0讲义:编写循环和条件任务”的版本间的差异

来自CloudWiki
跳转至: 导航搜索
第222行: 第222行:
 
... output omitted
 
... output omitted
 
</nowiki>
 
</nowiki>
 +
 +
==有条件地运行任务==
 +
Ansible可使用conditionals在符合特定条件时执行任务或play。例如,可以利用一个条件在 Ansible安装或配置服务前确定受管主机上的可用内存。
 +
 +
管理员可利用条件来区分不同的受管主机,并根据它们所符合的条件来分配功能角色。Playbook变 量、注册的变量和Ansible事实都可通过条件来进行测试。可以使用比较字符串、数字数据和布尔 值的运算符。
 +
 +
以下场景说明了在Ansible中使用条件的情况:
 +
 +
*可以在变量中定义硬限制(如min_memory)并将它与受管主机上的可用内存进行比较。
 +
*Ansible可以捕获并评估命令的输出,以确定某一任务在执行进一步操作前是否已经完成。例如, 如果某一程序失败,则将跳过批处理。
 +
*可以利用Ansible事实来确定受管主机网络配置,并且决定要发送的模板文件(如,网络绑定或 中继)。
 +
*可以评估CPU的数量,来确定如何正确调节某一Web服务器。
 +
*将注册的变量与预定义的变量进行比较,以确定服务是否已更改。例如,测试服务配置文件的 MD5校验和以查看服务是否已更改。
 +
 +
===条件任务语法===
 +
when语句用于有条件地运行任务。它取要测试的条件作为值。如果条件满足,则运行任务。如何 条件不满足,则跳过任务。
 +
可以测试的一个最简单条件是某一布尔变量是true还是falseo以下示例中的when语句导致任务仅 在 run_my_task 为 true 时运行:
 +
 +
<nowiki>
 +
- name: Simple Boolean Task Demo
 +
  hosts: all
 +
  vars:
 +
    run_my_task: true
 +
  tasks:
 +
    - name: httpd package is installed
 +
      yum:
 +
      name: httpd
 +
      when: run_my_task</nowiki>
 +
 +
以下示例稍微复杂一些,它测试my_service变量是否具有值。若有值,则将my_service的值 用作要安装的软件包的名称。如果未定义my_service变量,则跳过任务且不显示错误。
 +
 +
<nowiki>
 +
- name: Test Variable is Defined Demo
 +
  hosts: all
 +
  vars:
 +
    my_service: httpd
 +
  tasks:
 +
    - name: ”{{ my_service }} package is installed"
 +
      yum:
 +
        name: "{{ my_service })"
 +
      when: my_service is defined</nowiki>
 +
 +
下表显示了管理员在处理条件时可使用的一些运算:
 +
 +
<nowiki>示例条件
 +
操作 示例
 +
等于(值为字符串) ansible_machine == "x86_64"
 +
等于(值为数字) max_memory == 512
 +
小于 min_memory < 128
 +
大于 min_memory > 256
 +
小于等于 min_memory <= 256
 +
大于等于 min_memory >= 512
 +
不等于 min_memory != 512
 +
变量存在 min_memory is defined
 +
变量不存在 min_memory is not defined
 +
布尔变量是true、l、 True 或yes的求值为trueo  memory_available
 +
 +
布尔变量是false、0、False 或 no 的 求值为false  not memory_available
 +
第一个变量的值存在,作为第二个变量的列表中的值 ansible_distribution in supported_distros</nowiki>
 +
 +
上表中的最后一个条目初看起来有些令人困惑。下例演示了它的工作原理。
 +
 +
在示例中,ansible_distribution变量是在Gathering Facts任务期间确定的事 实,用于标识托管主机的操作系统分发。变量supported_distros由playbook作者创 建,包含该playbook支持的操作系统分发列表。如果ansible_distribution的值在 supported_distros列表中,则条件通过且任务运行。
 +
 +
<nowiki>
 +
---
 +
- name: Demonstrate the "in" keyword
 +
  hosts: all
 +
  gather_facts: yes
 +
  vars:
 +
    supported_distros:
 +
      - RedHat
 +
      - Fedora
 +
  tasks:
 +
    - name: Install httpd using yum, where supported
 +
      yum:
 +
        name: http
 +
        state: present
 +
      when: ansible_distribution in supported_distros</nowiki>
 +
 +
====重要====
 +
注意when语句的缩进。由于when语句不是模块变量,它必须通过缩进到任务的最 高级别,放置在模块的外面。
 +
 +
任务是YAML散列/字典,when语句只是任务中的又一个键,就如任务的名称以及它 所使用的模块一样。通常的惯例是将可能存在的任何when关键字放在任务名称和模 块(及模块参数)的后面。
 +
 +
===测试多个条件===
 +
一个when语句可用于评估多个条件。为此,可以使用and或or关键字组合条件,并使用括号分 组条件。
 +
 +
下列代码片段显示了如何表达多个条件的一些示例。
 +
 +
*如果任一条件为真时满足条件语句,则应当使用or语句。例如,如果计算机上运行的是红帽企 业Linux或Fedora,则下述条件得到满足:
 +
 +
when: ansible_distribution == "RedHat" or ansible_distribution == "Fedora"
 +
 +
使用and运算时,两个条件都必须为真,才能满足整个条件语句。例如,如果远程主机是红帽企 业Linux 7.5主机,并且安装的内核是指定版本,则将满足以下条件:
 +
 +
when: ansible_distribution_version == ”7.5" and ansible_kernel ==
 +
"3.10.0-327.el7.X86_64"
 +
 +
when关键字还支持使用列表来描述条件列表。向when关键字提供列表时,将使用and运算组 合所有条件。下面的示例演示了使用and运算符组合多个条件语句的另一方式:
 +
 +
<nowiki>
 +
when:
 +
  - ansible_distribution_version == "7.5"
 +
  - ansible_kernel == "3.10.0-327.el7.x86_64"</nowiki>
 +
 +
这种格式提高了可读性,而可读性是良好编写的Ansible Playbook的关键目标。
 +
 +
通过使用括号分组条件,可以表达更复杂的条件语句。这可确保正确解释它们。
 +
 +
例如,如果计算机上运行的是红帽企业Linux 7或Fedo「a28,则下述条件语句得到满足。此示例 使用大于字符(>),这样长条件就可以在playbook中分成多行,以便于阅读。
 +
 +
<nowiki>when: >
 +
(ansible_distribution == "RedHat" and
 +
ansible_distribution_major_version == "7")
 +
or
 +
(ansible_distribution == "Fedora" and
 +
ansible_distribution_major_version == "28")</nowiki>

2021年2月15日 (一) 08:59的版本

利用循环迭代任务

通过利用循环,管理员无需编写多个使用同一模块的任务。例如,他们不必编写五个任务来确保存在五个用户,而是只需编写一个任务来对含有五个用户的列表迭代,从而确保它们都存在。

Ansible支持使用loop关键字对一组项目迭代任务。您可以配置循环以利用列表中的各个项目、列表中各个文件的内容、生成的数字序列或更为复杂的结构来重复任务。本节介绍迭代项目列表的简单循环。有关更高级的循环方案,请参阅文档。

简单循环

简单循环对一组项目迭代任务。loop关键字添加到任务中,将应对其迭代任务的项目列表取为值。循环变量item保存每个迭代过程中使用的值。

请思考以下代码片段,它使用两次service模块来确保两个网络服务处于运行状态:

- name: Postfix is running
  service:
    name: postfix
    state: started

- name: Dovecot is running
  service:
    name: dovecot
    state: started

这两个任务可以重新编写为使用一个简单循环,从而只需一个任务来确保两个服务都在运行:

- name: Postfix and Dovecot are running service: name: "{{ item })" state: started loop: - postfix - dovecot

可以通过一个变量提供丄oop所使用的列表。在以下示例中'变量mail_services含有需要处于 运行状态的服务的列表。

vars:
   mail_services:
     - postfix
     - dovecot

tasks:
  - name: Postfix and Dovecot are running
    service:
      name: "{( item })"
      state: started
      loop: ”{{ mail_services }}*'

循环散列或字典列表

loop列表不需要是简单值列表。在以下示例中,列表中的每个项实际上是散列或字典。示例中的 每个散列或字典具有两个键,即name和groups,当前item循环变量中每个键的值可以分别通过 item. name 和 item. groups变量来检索。

- name: Users exist and are in the correct groups
  user:
    name: ”{{ item.name }}"
    state: present
    groups: "(( item.groups })"
  loop:
    - name: jane
      groups: wheel
    - name: joe
      groups: root

上一任务的结果是用户jane存在且为组wheel的成员,并且用户joe存在且为组root的成员。


较早样式的循环关键字

在Ansible 2.5之前,大多数playbook使用不同的循环语法。提供了多个循环关键字,前缀为 with_ ,后跟Ansible查找插件(一项高级功能,本课程中未加以详细介绍)的名称。这种循环语 法在现有playbook中很常见,但在将来的某个时候可能会被弃用。

下表列出了几个示例:

较早样式的Ansible循环

循环关键字	描述
with_items	行为与简单列表的loop关键字相同,例如字符串列表或散列/字典 列表。但与loop不同的是,如果为with_items提供了列表的列 表,它们将被扁平化为单级列表。循环变量item保存每次迭代过 程中使用的列表项。
with_file	此关键字需要控制节点文件名列表。循环变量item在每次迭代过 程中保存文件列表中相应文件的内容。
with_sequence	此关键字不需要列表,而是需要参数来根据数字序列生成值列表。 循环变量item在每次迭代过程中保存生成的序列中的一个生成项 的值。


playbook中的with_items的示例如下所示:

vars: data: - user0 - user1 - user2 tasks: - name: "with_items" debug: msg: "{{ item }}" with_items: "(( data }}"

重要

从Ansible 2.5开始,建议使用loop关键字编写循环。

但是,您仍然应该了解旧语法,尤其是with_items,因为它广泛用于现有 playbooko您可能会遇到继续使用with_*关键字进行循环的playbook和角色。

使用旧语法的任何任务都可以转换为结合使用loop和Ansible过滤器。您不需要知道如何使用 Ansible过滤器来执行此操作。有关如何将旧循环转换为新语法,以及如何循环不是简单列表的 项的示例,Ansible 用户指南第从 with_X迁移至循环[https://docs.ansible.com/ansible/latest/user_guide/playbooks_loops.html#migrating-from-with-x-to-loop]节中的 Ansible 文档提供了 良好 的参善。

您可能会遇到包含with_*关键字的旧playbook中的任务。

高级循环技术不在本课程的讨论范围内。本课程中的所有迭代任务都可以使用withjtems或 loop关键字实施。

将Register变量与Loop —起使用

register关键字也可以捕获循环任务的输岀。以下代码片段显示了循环任务中register变量的结 构:

[student@workstation loopdemo]$ cat loop_register.yml

- name: Loop Register Test

 gather_facts: no
 hosts: localhost
 tasks:
  - name: Looping Echo Task
    shell: "echo This is my item: {{ item })"
    loop:
      - one
      - two
    register: echo_results (1)
  - name: Show echo_results variable
    debug:
      var: echo_results (2)

(1) echo_results变量已注册。

(2)echo_results变量的内容显示在屏幕上。

运行上面的playbook会产生以下输出:

[student@workstation loopdemo]$ ansible-playbook loop_register.yml play

[Loop Register Test] ****************************************
TASK [Looping Echo Task] ************************** ... output omitted
TASK [Show echo_results variable] ***************** ok: [localhost] => {
"echo_results": {。
"changed": true,
"msg": "All items completed",
"results" : [O
"_ansible_ignore_errors": null,
... output omitted
"changed": true,
"cmd": "echo This is my item: one",
"delta": "0:00:00.011865",
"end": "2018-11-01 16:32:56.080433", "failed": false,
...output omitted
"item": "one",
"re": 0,
"start": "2018-11-01 16:32:56.068568", "stderr":
"stderr_lines":
"stdout": "This
"stdout_lines":
"This is my
]
},
{O
"_ansible_ignore_errors": null,
... output omitted
"changed": true,
"cmd": "echo This is my item: two", "delta": "0:00:00.011142"z
"end": "2018-11-01 16:32:56.828196", "failed": false,
... output omitted
"item": "two",
"rc": 0,
"start": "2018-11-01 16:32:56.817054", "stderr" : ,,n,
"stderr_lines":
"stdout": "This
"stdout_lines":
"This is my
]
}
[],
is my item: one",
item: one
口,
is my item: two",
item: two"
]❺
}
}
... output omitted

{字符表示echo_results变量的开头由键值对组成。

results譴包含上一个任务的结果。[字符表示列表的开头。

第一项的任务元数据的开头(由item键表示)。echo命令的输出可在stdout键中找至U。 第二项的任务结果元数据的幵头。

]字符表示results列表的结尾。

在上面,results键包含一个列表。在下面,修改了 playbook,使第二个任务迭代此列表:

[student@workstation loopdemo]$ cat new_loop_register.yml

- name: Loop Register Test
  gather_facts: no
  hosts: localhost
  tasks:
   - name: Looping Echo Task
     shell: "echo This is my item: {{ item })"
     loop:
       - one
       - two
     register: echo_results 
   - name: Show stdout from the previous task.
     debug:
       msg: "STDOUT from previous task: {( item.stdout })" 
     loop: "{{ echo_results['results'] }}"

执行上述playbook后,输出为:

PLAY [Loop Register Test] *****************
TASK [Looping Echo Task] ***************************************************** …output omitted
TASK [Show stdout from the previous task.] ******************** ok:
[localhost]=> "msg": "STDOUT
(item={...output omitted...=> { from previous task: This is my item: one
}
ok:
[localhost]=> "msg": "STDOUT
(item={...output omitted...)) => { from previous task: This is my item: two"
}
... output omitted

有条件地运行任务

Ansible可使用conditionals在符合特定条件时执行任务或play。例如,可以利用一个条件在 Ansible安装或配置服务前确定受管主机上的可用内存。

管理员可利用条件来区分不同的受管主机,并根据它们所符合的条件来分配功能角色。Playbook变 量、注册的变量和Ansible事实都可通过条件来进行测试。可以使用比较字符串、数字数据和布尔 值的运算符。

以下场景说明了在Ansible中使用条件的情况:

  • 可以在变量中定义硬限制(如min_memory)并将它与受管主机上的可用内存进行比较。
  • Ansible可以捕获并评估命令的输出,以确定某一任务在执行进一步操作前是否已经完成。例如, 如果某一程序失败,则将跳过批处理。
  • 可以利用Ansible事实来确定受管主机网络配置,并且决定要发送的模板文件(如,网络绑定或 中继)。
  • 可以评估CPU的数量,来确定如何正确调节某一Web服务器。
  • 将注册的变量与预定义的变量进行比较,以确定服务是否已更改。例如,测试服务配置文件的 MD5校验和以查看服务是否已更改。

条件任务语法

when语句用于有条件地运行任务。它取要测试的条件作为值。如果条件满足,则运行任务。如何 条件不满足,则跳过任务。 可以测试的一个最简单条件是某一布尔变量是true还是falseo以下示例中的when语句导致任务仅 在 run_my_task 为 true 时运行:

- name: Simple Boolean Task Demo
  hosts: all
  vars:
    run_my_task: true
  tasks:
    - name: httpd package is installed
      yum:
      name: httpd
      when: run_my_task

以下示例稍微复杂一些,它测试my_service变量是否具有值。若有值,则将my_service的值 用作要安装的软件包的名称。如果未定义my_service变量,则跳过任务且不显示错误。

- name: Test Variable is Defined Demo
  hosts: all
  vars:
    my_service: httpd
  tasks:
    - name: ”{{ my_service }} package is installed" 
      yum:
        name: "{{ my_service })"
      when: my_service is defined

下表显示了管理员在处理条件时可使用的一些运算:

示例条件
操作	示例
等于(值为字符串)	ansible_machine == "x86_64"
等于(值为数字)	max_memory == 512
小于	min_memory < 128
大于	min_memory > 256
小于等于	min_memory <= 256
大于等于	min_memory >= 512
不等于	min_memory != 512
变量存在	min_memory is defined
变量不存在	min_memory is not defined
布尔变量是true、l、 True 或yes的求值为trueo   memory_available

布尔变量是false、0、False 或 no 的 求值为false   not memory_available
第一个变量的值存在,作为第二个变量的列表中的值	ansible_distribution in supported_distros

上表中的最后一个条目初看起来有些令人困惑。下例演示了它的工作原理。

在示例中,ansible_distribution变量是在Gathering Facts任务期间确定的事 实,用于标识托管主机的操作系统分发。变量supported_distros由playbook作者创 建,包含该playbook支持的操作系统分发列表。如果ansible_distribution的值在 supported_distros列表中,则条件通过且任务运行。

--- - name: Demonstrate the "in" keyword hosts: all gather_facts: yes vars: supported_distros: - RedHat - Fedora tasks: - name: Install httpd using yum, where supported yum: name: http state: present when: ansible_distribution in supported_distros

重要

注意when语句的缩进。由于when语句不是模块变量,它必须通过缩进到任务的最 高级别,放置在模块的外面。

任务是YAML散列/字典,when语句只是任务中的又一个键,就如任务的名称以及它 所使用的模块一样。通常的惯例是将可能存在的任何when关键字放在任务名称和模 块(及模块参数)的后面。

测试多个条件

一个when语句可用于评估多个条件。为此,可以使用and或or关键字组合条件,并使用括号分 组条件。

下列代码片段显示了如何表达多个条件的一些示例。

  • 如果任一条件为真时满足条件语句,则应当使用or语句。例如,如果计算机上运行的是红帽企 业Linux或Fedora,则下述条件得到满足:
when: ansible_distribution == "RedHat" or ansible_distribution == "Fedora"

使用and运算时,两个条件都必须为真,才能满足整个条件语句。例如,如果远程主机是红帽企 业Linux 7.5主机,并且安装的内核是指定版本,则将满足以下条件:

when: ansible_distribution_version == ”7.5" and ansible_kernel ==

"3.10.0-327.el7.X86_64"

when关键字还支持使用列表来描述条件列表。向when关键字提供列表时,将使用and运算组 合所有条件。下面的示例演示了使用and运算符组合多个条件语句的另一方式:

when:
  - ansible_distribution_version == "7.5"
  - ansible_kernel == "3.10.0-327.el7.x86_64"

这种格式提高了可读性,而可读性是良好编写的Ansible Playbook的关键目标。

通过使用括号分组条件,可以表达更复杂的条件语句。这可确保正确解释它们。

例如,如果计算机上运行的是红帽企业Linux 7或Fedo「a28,则下述条件语句得到满足。此示例 使用大于字符(>),这样长条件就可以在playbook中分成多行,以便于阅读。

when: >
(ansible_distribution == "RedHat" and
ansible_distribution_major_version == "7")
or
(ansible_distribution == "Fedora" and
ansible_distribution_major_version == "28")