通常,play的结果可能取决于变量的值,facts(有关远程系统的知识)或先前的任务结果。 在某些情况下,变量的值可能取决于其他变量。 此外,可以创建其他组,以根据主机是否与其他条件匹配来管理主机。 在Ansible中有许多控制执行流程的选项。 支持条件的更多示例可以在这里找到: http : //jinja.pocoo.org/docs/dev/templates/#comparisons

When 语句

有时您会想要跳过特定主机上的特定步骤。 如果操作系统是特定版本,这可能是一个简单的方法,如果没有安装某个包,或者如果文件系统正在充满,可能会执行一些清理步骤。

这在使用when子句很容易做到,它包含一个没有双大括号的原始Jinja2表达式(见变量 )。 其实很简单:

tasks:
  - name: "shut down Debian flavored systems"
    command: /sbin/shutdown -t now
    when: ansible_os_family == "Debian"
    # note that Ansible facts and vars like ansible_os_family can be used
    # directly in conditionals without double curly braces

您也可以使用括号来分组条件:
tasks:
  - name: "shut down CentOS 6 and Debian 7 systems"
    command: /sbin/shutdown -t now
    when: (ansible_distribution == "CentOS" and ansible_distribution_major_version == "6") or
          (ansible_distribution == "Debian" and ansible_distribution_major_version == "7")
所有需要为真的多个条件(逻辑“和”)也可以指定为列表:
asks:
  - name: "shut down CentOS 6 systems"
    command: /sbin/shutdown -t now
    when:
      - ansible_distribution == "CentOS"
      - ansible_distribution_major_version == "6"

一些Jinja2“过滤器”也可以在语句中使用,其中一些是唯一的,由Ansible提供。 假设我们想忽略一个语句的错误,然后决定根据成功或失败有条件地做某些事情:
tasks:
  - command: /bin/false
    register: result
    ignore_errors: True

  - command: /bin/something
    when: result|failed

  # In older versions of ansible use |success, now both are valid but succeeded uses the correct tense.
  - command: /bin/something_else
    when: result|succeeded

  - command: /bin/still/something_else
    when: result|skipped


请注意,这是“register”声明的一点点预示。 我们稍后会在本章中讨论。

提醒您,要查看特定系统上有哪些facts,您可以执行以下操作:

ansible hostname.example.com -m setup

提示:有时您会收到一个字符串的变量,您将需要对其进行数学运算比较。 你可以这样做:

tasks:
  - shell: echo "only on Red Hat 6, derivatives, and later"
    when: ansible_os_family == "RedHat" and ansible_lsb.major_release|int >= 6


也可以使用在playbooks或inventory中定义的变量。 一个例子可能是基于一个变量的布尔值执行一个任务:
vars:
  epic: true

那么条件执行可能如下所示:
tasks:
    - shell: echo "This certainly is epic!"
      when: epic
要么:
tasks:
    - shell: echo "This certainly isn't epic!"
      when: not epic

如果未设置必需变量,则可以使用Jinja2 定义的测试来跳过或失败。 例如:
tasks:
    - shell: echo "I've got '{{ foo }}' and am not afraid to use it!"
      when: foo is defined

    - fail: msg="Bailing out. this play requires 'bar'"
      when: bar is undefined

这与vars文件的条件导入(见下文)相结合特别有用。 如示例所示,您不需要使用{{}}在条件中使用变量,因为这些变量已经被隐含。

Loops and Conditionals

结合with_items (请参阅循环 ),请注意,每个项目分别处理when语句。 这是设计:

tasks:
    - command: echo {{ item }}
      with_items: [ 0, 2, 4, 6, 8, 10 ]
      when: item > 5


如果需要根据定义的循环变量跳过整个任务,则使用| default过滤器来提供一个空的迭代器:
- command: echo {{ item }}
  with_items: "{{ mylist|default([]) }}"
  when: item > 5

如果使用没有列表的with_dict
- command: echo {{ item.key }}
  with_dict: "{{ mydict|default({}) }}"
  when: item.value > 5

Loading in Custom Facts

如果需要,提供自己的facts也很容易,这在开发模块中有所体现。 要运行它们,只需在您的任务列表顶部调用您自己的自定义facts收集模块,返回的变量将在以后的任务中访问:

tasks:
    - name: gather site specific fact data
      action: site_facts
    - command: /usr/bin/thingy
      when: my_custom_fact_just_retrieved_from_the_remote_system == '1234'

Applying ‘when’ to roles and includes

请注意,如果您有多个任务共享相同的条件语句,则可以将条件附加到如下的任务include语句。 所有的任务得到评估,但条件应用于每个任务:

- include: tasks/sometasks.yml
  when: "'reticulating splines' in output"

- hosts: webservers
  roles:
     - { role: debian_stock_config, when: ansible_os_family == 'Debian' }

在不符合条件的系统上使用此方法时,默认情况下,您将会注意到很多“跳过”输出。 在“ 关于模块”文档中的“group_by”模块上阅读,以获得更加精简的方式来完成相同的操作。

Conditional Imports

有时你会根据某些标准,在一个playbook里做一些不同的事情。 有一本适用于多个平台和操作系统版本的playbook就是一个很好的例子。

例如,CentOS和Debian之间的Apache软件包的名称可能不同,但可以在一个可读的Playbook中使用最少的语法进行处理:

---
- hosts: all
  remote_user: root
  vars_files:
    - "vars/common.yml"
    - [ "vars/{{ ansible_os_family }}.yml", "vars/os_defaults.yml" ]
  tasks:
  - name: make sure apache is started
    service: name={{ apache }} state=started


作为提醒,各种YAML文件只包含键值和值:

---
# for vars/CentOS.yml
apache: httpd
somethingelse: 42

这个怎么用? 如果操作系统是“CentOS”,则第一个可以导入的文件是“vars / CentOS.yml”,如果该文件不存在,则为“/vars/os_defaults.yml”。
如果列表中没有找到任何文件,则会出现错误。 在Debian上,它会首先查看'vars / Debian.yml'而不是'vars / CentOS.yml',然后再回到'vars / os_defaults.yml'。 很简单
要使用这个条件导入功能,您需要在运行该playbook之前安装facter或ohai,但如果您喜欢,您可以将其与Ansible一起推出:
# for facter
ansible -m yum -a "pkg=facter state=present"
ansible -m yum -a "pkg=ruby-json state=present"

# for ohai
ansible -m yum -a "pkg=ohai state=present"

可配置的配置方法 - 将变量与任务分离,使您的剧本不会变成具有丑陋的嵌套ifs,条件等的任意代码,并导致更精简和可审计的配置规则,特别是因为至少有一个决策点要跟踪。

Selecting Files And Templates Based On Variables

有时您要复制的配置文件或您将使用的模板可能取决于变量。 以下构造选择适合于给定主机的变量的第一个可用文件,这通常比在模板中放入大量条件更为干净。

以下示例显示如何模板化出与CentOS和Debian之间有很大不同的配置文件:

- name: template a file
  template: src={{ item }} dest=/etc/myapp/foo.conf
  with_first_found:
    - files:
       - {{ ansible_distribution }}.conf
       - default.conf
      paths:
       - search_location_one/somedir/
       - /opt/other_location/somedir/

Register Variables

通常在playbook中,将给定命令的结果存储在变量中可能是有用的,然后再访问它。 以这种方式使用命令模块可以在很多方面消除编写站点特定事实的需要,例如,您可以测试特定程序的存在。

'register'关键字决定将结果保存到哪个变量中。生成的变量可以在模板,动作行或when语句中使用。 看起来像这样(显而易见的例子):

- name: test play
  hosts: all

  tasks:

      - shell: cat /etc/motd
        register: motd_contents

      - shell: echo "motd contains the word hi"
        when: motd_contents.stdout.find('hi') != -1

如前所示,注册变量的字符串内容可通过'stdout'值访问。 注册结果可以在任务的“with_item”中被使用,如果它被转换成列表(或已经是列表),如下所示。
“stdout_lines”也可以在对象上使用,但是如果需要也可以调用“home_dirs.stdout.split()”,并且可以被其他字段分割:
- name: registered variable usage as a with_items list
  hosts: all

  tasks:

      - name: retrieve the list of home directories
        command: ls /home
        register: home_dirs

      - name: add home dirs to the backup spooler
        file: path=/mnt/bkspool/{{ item }} src=/home/{{ item }} state=link
        with_items: "{{ home_dirs.stdout_lines }}"
        # same as with_items: "{{ home_dirs.stdout.split() }}"

如前所示,注册变量的字符串内容可通过'stdout'值访问。 您可以检查注册变量的字符串内容为空:
- name: check registered variable for emptiness
  hosts: all

  tasks:

      - name: list contents of directory
        command: ls mydir
        register: contents

      - name: check contents for emptiness
        debug: msg="Directory is empty"
        when: contents.stdout == ""