ansible 五、playbook(条件与循环)

大番茄 2020年02月14日 2,514次浏览

一、条件判断

https://docs.ansible.com/ansible/latest/user_guide/playbooks_conditionals.html#the-when-statement
when 子句。 子句里的变量不需要 {{ }}。

1、基本格式

tasks:
  - name: "shut down Debian flavored systems"
    command: /sbin/shutdown -t now
    when: ansible_facts['os_family'] == "Debian"
    # note that all variables can be used directly in conditionals without double curly braces

只有在目标主机系统是Debian的时候才会执行。不符合条件会跳过。

TASK [shut down Debian flavored systems] **************************************************************************************************************************************
skipping: [172.100.102.92]
skipping: [172.100.102.93]

2、复杂条件

使用and,or与not逻辑符号。

and, or

tasks:
  - name: "shut down CentOS 6 and Debian 7 systems"
    command: /sbin/shutdown -t now
    when: (ansible_facts['distribution'] == "CentOS" and ansible_facts['distribution_major_version'] == "6") or
          (ansible_facts['distribution'] == "Debian" and ansible_facts['distribution_major_version'] == "7")

还可以写成列表,各个列表项之间是and关系。

  - name: "shut down CentOS 6 systems"
    command: /sbin/shutdown -t now
    when:
      - ansible_facts['distribution'] == "CentOS"
      - ansible_facts['distribution_major_version'] == "6"

就是
ansible_facts['distribution'] == "CentOS" and ansible_facts['distribution_major_version'] == "6"

not

when: not (ansible_facts['distribution'] == "CentOS" and ansible_facts['distribution_major_version'] == "6")

3、判断变量是否定义

is undefined
判断变量是否没有定义。这里的ansible_facts是facts变量,肯定是定义了的。

  tasks:
  - name: "shut down CentOS 6 systems"
    command: echo 'hello world'
    when: ansible_facts is undefined

is defined
判断变量是否定义了。

when: ansible_facts is defined

还有 is not , 上面的 is undefined 也可以写成 is not defined

4、循环加判断

循环与判断一起, 则每次循环都会判断一次。
如:

- hosts: 172.100.102.90
  tasks:
  - name: test
    debug: msg={{item}}
    loop: [0, 2, 4, 6, 8, 10]
    when: item > 5
TASK [test] *******************************************************************************************************************************************************************
skipping: [172.100.102.90] => (item=0)
skipping: [172.100.102.90] => (item=2)
skipping: [172.100.102.90] => (item=4)
ok: [172.100.102.90] => (item=6) => {
    "msg": 6
}
ok: [172.100.102.90] => (item=8) => {
    "msg": 8
}
ok: [172.100.102.90] => (item=10) => {
    "msg": 10
}

二、循环

ansible 2.5添加了loop子句,用来代替with_X
with_X 还可以使用, 但推荐使用 loop
with_list 可以完全替换成loop
with_items 的扁平化方面需要加过滤器

循环中产生的元素使用变量item表示。

区别

下面直接传入列表数据跟调用一个包含列表的变量是一样的。

1、with_list --> loop

with_list:

  tasks:
  - name: test
    debug: msg={{item}}
    with_list:
      - one
      - two
      - [4,5]
      - {'a': 1, 'b': 2}

输出结果是这样:

TASK [test] *******************************************************************************************************************************************************************
ok: [172.100.102.90] => (item=one) => {
    "msg": "one"
}
ok: [172.100.102.90] => (item=two) => {
    "msg": "two"
}
ok: [172.100.102.90] => (item=[4, 5]) => {
    "msg": [
        4,
        5
    ]
}
ok: [172.100.102.90] => (item={u'a': 1, u'b': 2}) => {
    "msg": {
        "a": 1,
        "b": 2
    }
}

可以完全替换成loop, 输出结构一样。

  tasks:
  - name: test
    debug: msg={{item}}
    loop:
      - one
      - two
      - [4,5]
      - {'a': 1, 'b': 2}

4、with_items --> loop

with_items:

  tasks:
  - name: test
    debug: msg={{item}}
    with_items:
      - one
      - two
      - [4,5]
      - {'a': 1, 'b': 2}

输出结果:

TASK [test] *******************************************************************************************************************************************************************
ok: [172.100.102.90] => (item=one) => {
    "msg": "one"
}
ok: [172.100.102.90] => (item=two) => {
    "msg": "two"
}
ok: [172.100.102.90] => (item=4) => {
    "msg": 4
}
ok: [172.100.102.90] => (item=5) => {
    "msg": 5
}
ok: [172.100.102.90] => (item={u'a': 1, u'b': 2}) => {
    "msg": {
        "a": 1,
        "b": 2
    }
}

可以看到[4,5]给扁平化了,分别循环了4个5。
loop 本身没有这个功能,想要达到相同的效果只能在调用变量的时候给变量加个过滤器。
这样做的原因其实只是因为 原来的变量数据不能或是不好改。
因为需要用变量,所以需要修改一下:

- hosts: 172.100.102.90
  vars:
    vara: ['one', 'two', [4, 5], {'a': 1, 'b': 2}]
  tasks:
  - name: test
    debug: msg={{item}}
    with_items: "{{ vara }}"

执行结果跟上面一样。

修改成loop:

- hosts: 172.100.102.90
  vars:
    vara: ['one', 'two', [4, 5], {'a': 1, 'b': 2}]
  tasks:
  - name: test
    debug: msg={{item}}
    loop: "{{ vara | flatten(1)}}"

结果一样。
上面只是说明两者的区别,以及如果要复用的话该怎么办。
最好的方式还是修改成符合loop使用的数据。

例子

贴几个官方loop的例子

1、遍历列表

- name: add several users
  user:
    name: "{{ item }}"
    state: present
    groups: "wheel"
  loop:
     - testuser1
     - testuser2

循环添加用户testuser1, testuser2。 不使用循环的话相当于:

- name: add user testuser1
  user:
    name: "testuser1"
    state: present
    groups: "wheel"

- name: add user testuser2
  user:
    name: "testuser2"
    state: present
    groups: "wheel"

2、遍历哈希列表

添加用户,并且指定用户组。

- name: add several users
  user:
    name: "{{ item.name }}"
    state: present
    groups: "{{ item.groups }}"
  loop:
    - { name: 'testuser1', groups: 'wheel' }
    - { name: 'testuser2', groups: 'root' }

3、遍历字典

要注意,上面两个例子,提供的数据都是列表。
所以循环就可以直接使用。
如果提供的数据狮子点,需要使用过滤器转换成列表。
比如:

- hosts: 172.100.102.90
  vars:
    vara: {'name': 'testuser1', 'gender': 'M', 'age': 29, 'ps': ''}
  tasks:
  - name: test
    debug: msg={{item}}
    loop: "{{ vara }}"

是会报错的:

TASK [test] *******************************************************************************************************************************************************************
fatal: [172.100.102.90]: FAILED! => {"msg": "Invalid data passed to 'loop', it requires a list, got this instead: {u'gender': u'M', u'age': 29, u'name': u'testuser1', u'ps': u''}. Hint: If you passed a list/dict of just one element, try adding wantlist=True to your lookup invocation or use q/query instead of lookup."}

无效的loop数据。需要提供list类型数据。
添加字典过滤器dict2items

- hosts: 172.100.102.90
  vars:
    vara: {'name': 'testuser1', 'gender': 'M', 'age': 29, 'ps': ''}
  tasks:
  - name: test
    debug: msg={{item}}
    loop: "{{ vara | dict2items }}"
TASK [test] *******************************************************************************************************************************************************************
ok: [172.100.102.90] => (item={'key': u'gender', 'value': u'M'}) => {
    "msg": {
        "key": "gender",
        "value": "M"
    }
}
ok: [172.100.102.90] => (item={'key': u'age', 'value': 29}) => {
    "msg": {
        "key": "age",
        "value": 29
    }
}
ok: [172.100.102.90] => (item={'key': u'name', 'value': u'testuser1'}) => {
    "msg": {
        "key": "name",
        "value": "testuser1"
    }
}
ok: [172.100.102.90] => (item={'key': u'ps', 'value': u''}) => {
    "msg": {
        "key": "ps",
        "value": ""
    }
}

可以看到,把每一组key,value都取出来了。

4、条件判断

根据上面的例子,添加用户name。
在循环的过程中,只有当key等于name的时候添加。

- hosts: 172.100.102.90
  vars:
    vara: {'name': 'testuser1', 'gender': 'M', 'age': 29, 'ps': ''}
  tasks:
  - name: test
    user:
      name: "{{ item.value }}"
      state: present
    loop: "{{ vara | dict2items }}"
    when: item.key == "name"

执行结果:

TASK [test] *******************************************************************************************************************************************************************
skipping: [172.100.102.90] => (item={'key': u'gender', 'value': u'M'})
skipping: [172.100.102.90] => (item={'key': u'age', 'value': 29})
changed: [172.100.102.90] => (item={'key': u'name', 'value': u'testuser1'})
skipping: [172.100.102.90] => (item={'key': u'ps', 'value': u''})

只有key==name的执行了, 其他都跳过了。 我们只需要name来创建用户。

注册变量

循环中的注册变量与非循环产生的注册变量数据结构有点不同,循环中的注册变量包含每次循环产生的数据。

- shell: "echo {{ item }}"
  loop:
    - "one"
    - "two"
  register: echo

还有好多辅助的方法,如: 每次循环暂停一段时间, 限制输出, 循环进度 等等。
感觉暂时都用不到,有兴趣的话:
https://docs.ansible.com/ansible/latest/user_guide/playbooks_loops.html

官方提供的with_X替换成loop的方法

with_list

with_list直接由loop代替。

- name: with_list
  debug:
    msg: "{{ item }}"
  with_list:
    - one
    - two

- name: with_list -> loop
  debug:
    msg: "{{ item }}"
  loop:
    - one
    - two

with_items

with_items替换为loopflatten过滤器。

- name: with_items
  debug:
    msg: "{{ item }}"
  with_items: "{{ items }}"

- name: with_items -> loop
  debug:
    msg: "{{ item }}"
  loop: "{{ items|flatten(levels=1) }}"

with_indexed_items

with_indexed_items 替换成 loopflatten滤波器还有loop_control.index_var

- name: with_indexed_items
  debug:
    msg: "{{ item.0 }} - {{ item.1 }}"
  with_indexed_items: "{{ items }}"

- name: with_indexed_items -> loop
  debug:
    msg: "{{ index }} - {{ item }}"
  loop: "{{ items|flatten(levels=1) }}"
  loop_control:
    index_var: index

with_flattened

with_flattened替换为loopflatten过滤器。

- name: with_flattened
  debug:
    msg: "{{ item }}"
  with_flattened: "{{ items }}"

- name: with_flattened -> loop
  debug:
    msg: "{{ item }}"
  loop: "{{ items|flatten }}"

with_together

with_together替换为loopzip过滤器。

- name: with_together
  debug:
    msg: "{{ item.0 }} - {{ item.1 }}"
  with_together:
    - "{{ list_one }}"
    - "{{ list_two }}"

- name: with_together -> loop
  debug:
    msg: "{{ item.0 }} - {{ item.1 }}"
  loop: "{{ list_one|zip(list_two)|list }}"

with_dict

with_dict替换成loop和(dictsortdict2items)过滤器。

- name: with_dict
  debug:
    msg: "{{ item.key }} - {{ item.value }}"
  with_dict: "{{ dictionary }}"

- name: with_dict -> loop (option 1)
  debug:
    msg: "{{ item.key }} - {{ item.value }}"
  loop: "{{ dictionary|dict2items }}"

- name: with_dict -> loop (option 2)
  debug:
    msg: "{{ item.0 }} - {{ item.1 }}"
  loop: "{{ dictionary|dictsort }}"

with_sequence

with_sequence替换为looprange函数,并可能替换为format过滤器。

- name: with_sequence
  debug:
    msg: "{{ item }}"
  with_sequence: start=0 end=4 stride=2 format=testuser%02x

- name: with_sequence -> loop
  debug:
    msg: "{{ 'testuser%02x' | format(item) }}"
  # range is exclusive of the end point
  loop: "{{ range(0, 4 + 1, 2)|list }}"

with_subelements

with_subelements替换为loopsubelements过滤器。

- name: with_subelements
  debug:
    msg: "{{ item.0.name }} - {{ item.1 }}"
  with_subelements:
    - "{{ users }}"
    - mysql.hosts

- name: with_subelements -> loop
  debug:
    msg: "{{ item.0.name }} - {{ item.1 }}"
  loop: "{{ users|subelements('mysql.hosts') }}"

with_nested / with_cartesian

with_nestedwith_cartesian替换为loopproduct过滤器。

- name: with_nested
  debug:
    msg: "{{ item.0 }} - {{ item.1 }}"
  with_nested:
    - "{{ list_one }}"
    - "{{ list_two }}"

- name: with_nested -> loop
  debug:
    msg: "{{ item.0 }} - {{ item.1 }}"
  loop: "{{ list_one|product(list_two)|list }}"

with_random_choice

with_random_choice只需使用random过滤器即可替换,而无需使用loop

- name: with_random_choice
  debug:
    msg: "{{ item }}"
  with_random_choice: "{{ my_list }}"

- name: with_random_choice -> loop (No loop is needed here)
  debug:
    msg: "{{ my_list|random }}"
  tags: random