Linux Note / 奇淫技巧

Ansible 让’青蛙’说出你的任务

Einic Yeo · 9月16日 · 2019年 ·

1.让’青蛙’说出你的任务

# ubuntu/debian: apt install cowsay
$ yum -y install cowsay
$ export ANSIBLE_NOCOWS=0 ANSIBLE_COW_SELECTION=bud-frogs

版权声明:本文遵循 CC 4.0 BY-SA 版权协议,若要转载请务必附上原文出处链接及本声明,谢谢合作!执行任意playbook,你将在每个任务得到版权声明:本文遵循 CC 4.0 BY-SA 版权协议,若要转载请务必附上原文出处链接及本声明,谢谢合作!

$ ansible-playbook test.yml 
 __________________
< PLAY [localhost] >
 ------------------
     \
      \
          oO)-.                       .-(Oo
         /__  _\                     /_  __\
         \  \(  |     ()~()         |  )/  /
          \__|\ |    (-___-)        | /|__/
          '  '--'    ==`-'==        '--'  '
 
 ________________________
< TASK [Hello bud-frogs] >
 ------------------------
     \
      \
          oO)-.                       .-(Oo
         /__  _\                     /_  __\
         \  \(  |     ()~()         |  )/  /
          \__|\ |    (-___-)        | /|__/
          '  '--'    ==`-'==        '--'  '
 
ok: [localhost] => {
    "msg": "Hello"
}
 ____________
< PLAY RECAP >
 ------------
     \
      \
          oO)-.                       .-(Oo
         /__  _\                     /_  __\
         \  \(  |     ()~()         |  )/  /
          \__|\ |    (-___-)        | /|__/
          '  '--'    ==`-'==        '--'  '
 
localhost                  : ok=1    changed=0    unreachable=0    failed=0

2. 在shell模块中版权声明:本文遵循 CC 4.0 BY-SA 版权协议,若要转载请务必附上原文出处链接及本声明,谢谢合作!使用脚本写法与jinja2

# 我们知道,任何能写变量的地方都能嵌套jinja2表达式
# 而shell模块中的内容到客户端渲染后也是一个可执行脚本
# 所以我们可以在shell内编写比平时更复杂的脚本
---
- name: Run shell
  shell: |-
    {% if foo is defined %}
    mkdir -p /tmp/{{ foo }}
    {% else %}
    if [[ ! ${foo} ]];then
        mkdir -p /var/lib/{{ bar }}
        echo 'success'
    else
        echo 'falure'
    fi
    {% endif %} 

3.展开jinja2中的一行的表达式

# 在使用jiaja2 if判断一个变量的时候,为了防止空格跟换行,我们经常会写在一行判断,但是展示却不太友好
---
- set_fact:
    GET_REPO: "{% if ENV == 'UAT' %}[email protected]:xnph-devops/conf-test.git{% else %}[email protected]:xnph-devops/conf.git{% endif %}"
 
# 一行冗长的表达式看着很不直观,我们可以借助jinja2的‘-’实现换行写,并忽略换行符
# 其中:
# |- 为块标签忽略换行
# {%- -%} 为忽略换行 
---
- set_fact:
    GIT_REPO: |-
      {%- if ENV == 'UAT' -%}
      [email protected]:xnph-devops/conf-test.git
      {%- else -%}
      [email protected]:xnph-devops/conf.git
      {%- endif -%} 

4.简易的jinja2 ‘if else’表达式

# 我们在编写jinja2 if else的表达式时,通常都是像这样的:
# 下面例子,索引ID小于3的都分配为master
---
- set_fact:
      role: >-
        {%- if ansible_play_hosts.index(inventory_hostname) < 3 -%}
          master
        {%- else -%}
          slave
        {%- endif -%}
        
# 我们利用python特性,可以简写如下:
---
- set_fact:
      role: "{{ ['slave','master'][ansible_play_hosts.index(inventory_hostname) < 3] }}"
# 即,先判断ansible_play_hosts.index(inventory_hostname) < 3是否成立,
# 如果成立,即true,失败为false
# 而python中true==>1,false==>0
# 再取['slave','master']列表索引
# 其中ansible_play_hosts为当前任务执行的所有主机

5.获取当前时间

# 在setup中其实有时间字段,但是被拆分,或者格式问题不好处理,我们通过内置方法能更便捷的获取
# 格式可以自定义
- set_fact:
    datetime: "{{ '%Y-%d-%m:%H' | strftime() }}"
    
# 但是这样取的时间在客户端时间出现不一致的时候可能会出现问题,所以,我们通过另外一个办法
# 即:通过lookup插件(lookup插件工作在控制端本机),统一获取本机时间
- set_fact:
    datetime: "{{ lookup('pipe','date +%Y-%d-%m:%H') }}"

6.单独变量嵌套

# 假设我们有如下playbook:
---
- hosts: group_a
  vars:
    foo_var: foo
    bar_var: bar
 
  tasks:
    - debug: msg="I like the {{ {{ key }}_var }}"
    
# 以上执行必然是回报错的,
# 其中{{ key }}是我们想通过动态传入foo或者bar来获取foo_var或者bar_var的值
# 为此我们可以通过lookup的vars插件来查到目的,改造如下:
---
- hosts: group_a
  vars:
    foo_var: foo
    bar_var: bar
 
  tasks:
    - debug: msg="I like the {{ lookup('vars', key + '_var') }}"
    
# 执行传入key=foo
ansible-playbook test.yml -e 'key=foo'

7.字典变量嵌套

版权声明:本文遵循 CC 4.0 BY-SA 版权协议,若要转载请务必附上原文出处链接及本声明,谢谢合作!
# 假设我们有如下playbook
---
- hosts: group_a
  vars:
    project:
      foo: foo_value
      bar: bar_value
 
  tasks:
    - debug: msg="I like the {{ project.{{ key }} }}"
    
# 当然,以上执行是会报错的,
# 其中{{ key }}即为我想通过动态传入foo或者bar来获取project下的键值
# 为此我们可以这么写:
---
- hosts: group_a
  vars:
    project:
      foo: foo_value
      bar: bar_value
 
  tasks:
    - debug: msg="I like the {{ project[key] }}"
    
# 执行传入key=foo
ansible-playbook test.yml -e 'key=foo'

8.限制必须指定至少一个tags才能运行任务

# 我们对多个任务做了tags,而我们运行任务而不指定-t时,默认会运行所有的任务
# 有时候这并不是我们所期望的,所以想当不指定tags时候不运行后面的任务
---
- hosts: localhost
  gather_facts: false
 
  tasks:
 
  - fail:
      msg: "你必须指定一个tags[-t]"
    when: "ansible_run_tags == ['all']"
    run_once: true
    
  - debug: msg='hello {{ ansible_run_tags }}'
    tags:
    - abc
 
# 即,只要ansible_run_tags默认值为['all']时,任务就直接失败
$ ansible-playbook abc.yml 
 
PLAY [localhost] **********************************************************************************************************************************************************
 
TASK [fail] ***************************************************************************************************************************************************************
fatal: [localhost]: FAILED! => {"changed": false, "msg": "你必须指定一个tags[-t]"}
 
PLAY RECAP ****************************************************************************************************************************************************************
localhost                  : ok=0    changed=0    unreachable=0    failed=1
 
# 其中:
# ansible_run_tags为ansible内置变量,默认值为['all']

9.访问其他主机的变量

# A主机有变量foo=archive,B主机想使用A主机的{{ foo }}怎么办呢?
---
- hosts: A:B
  tasks:
      
  - debug: msg={{ hostvars['A']['foo'] }}
 
# 大部分的变量对象都存放在hostvars中

10.计算不同主机中相同变量的和

版权声明:本文遵循 CC 4.0 BY-SA 版权协议,若要转载请务必附上原文出处链接及本声明,谢谢合作!
# 什么意思呢,即,有N主机,有相同的变量score,但是值是不一样的,现在想计算它们的score和
# 假定inventory如下:
$ cat inventory.ini
[public]
10.18.12.213 score=77
10.18.16.166 score=100
10.18.16.167 score=81
10.18.16.168 score=56
 
# 计算score的和
---
- hosts: localhost
 
  tasks:
 
  - debug: msg={{ ansible_play_hosts_all | map('extract',hostvars,'score') | sum }}
  
# 执行结果
$ ansible-playbook -i inventory.ini score_sum.yml
 
PLAY [public] *************************************************************************************************************************************************************
 
TASK [Gathering Facts] ****************************************************************************************************************************************************
ok: [10.18.16.167]
ok: [10.18.16.166]
ok: [10.18.16.168]
ok: [10.18.12.213]
 
TASK [debug] **************************************************************************************************************************************************************
ok: [10.18.12.213] => {
    "msg": "314"
}
ok: [10.18.16.166] => {
    "msg": "314"
}
ok: [10.18.16.167] => {
    "msg": "314"
}
ok: [10.18.16.168] => {
    "msg": "314"
}
 
PLAY RECAP ****************************************************************************************************************************************************************
10.18.12.213               : ok=2    changed=0    unreachable=0    failed=0   
10.18.16.166               : ok=2    changed=0    unreachable=0    failed=0   
10.18.16.167               : ok=2    changed=0    unreachable=0    failed=0   
10.18.16.168               : ok=2    changed=0    unreachable=0    failed=0
 
# 其他例子:
# 获取所有主机的序列号,并以','连接
- debug: msg={{ ansible_play_hosts_all | map('extract',hostvars,'ansible_product_serial') | join(',') }}
 
# 获取所有主机内存的总和
- debug: msg={{ ansible_play_hosts_all | map('extract',hostvars,'ansible_memtotal_mb') | sum }}
 
# 其中:
# ansible_play_hosts_all为当前任务执行的所有主机
# map方法参考python map
# sum为计算list的和

11.使用本地命令的值作为循环参数

# 使用ls /tmp的输出作为loop循环的值
# 其中:q == query
# query与lookup都工作在本机
---
- hosts: localhost
  gather_facts:
 
  tasks:
 
  - debug: msg={{ item }}
    loop: "{{ q('lines','ls /tmp/') }}"

12.嵌套两个列表的循环任务

# 假定有2个列表
#  a_list: [1,2]
#  b_list: ['jack', 'green', 'apple']
# 需要嵌套循环输出:
# jack-1,,green-1,apple-1,jack-2,green-2...
---
- hosts: localhost
  vars:
    a_list: [1,2]
    b_list: ['jack', 'green', 'apple']
 
  gather_facts: false
 
  tasks:
 
  - debug: msg="{{ item[1] }}-{{ item[0] }}"
    loop: "{{ a_list | product(b_list) | list }}"
    
$ ansible-playbook loops.yml
 
PLAY [localhost] **********************************************************************************************************************************************************
 
TASK [debug] **************************************************************************************************************************************************************
ok: [localhost] => (item=[1, u'jack']) => {
    "msg": "jack-1"
}
ok: [localhost] => (item=[1, u'green']) => {
    "msg": "green-1"
}
ok: [localhost] => (item=[1, u'apple']) => {
    "msg": "apple-1"
}
ok: [localhost] => (item=[2, u'jack']) => {
    "msg": "jack-2"
}
ok: [localhost] => (item=[2, u'green']) => {
    "msg": "green-2"
}
ok: [localhost] => (item=[2, u'apple']) => {
    "msg": "apple-2"
}
 
PLAY RECAP ****************************************************************************************************************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0  

13.全局任务滚动执行

# 对于全局任务滚动执行
# 设置serial为1,或者其他的数值,会按这个数量的主机去滚动执行任务
# 每个任务结尾会停顿5s,再下一个主机
- hosts: public
  serial: 1
  gather_facts: false
  
  tasks:
    - name: I\'m the first task
      debug: msg="hello {{ inventory_hostname }}"
    
    - name: Sleep task
      wait_for:
        delay: 5
      when: (ansible_play_hosts_all.index[-1] != inventory_hostname)
# 对于最后一台主机,是不需要执行wait_for任务的,所以判断最后一台主机跳过wait_for
# 其中:
# serial为滚动执行的关键字,可以是数值或者百分比(%)
# ansible_play_hosts_all为当前任务执行的所有主机
# inventory_hostname为各个主机自己

14.部分任务滚动执行

# 把需要滚动执行的任务放入include_tasks中,
# 利用loop循环所有当前执行的主机(ansible_play_hosts_all),即:
# 只有放入other.yml的任务会一台一台执行
# 设置run_once避免每个主机都多次重复执行
---
- hosts: public
 
  tasks:
 
  - debug: msg="I'm the Top public task"
 
  - include_tasks: other.yml
    loop: "{{ ansible_play_hosts_all }}"
    loop_control:
      loop_var: target_host
    run_once: true
 
# other.yml
---
- block:
  - shell: hostname -I
    register: res
 
  - debug: msg="I'm {{ res.stdout }}"
 
  delegate_to: "{{ target_host }}"
  
# 其中:
# ansible_play_hosts_all为前期执行的所有host
# loop_control的loop_var把item赋值给target_host,即,target_host==item,防止other里其他任务调用loop,item变量冲突
# run_once:每个循环对象只允许一次,不设置的话,会重复运行
# delegate_to委派任务
# 另外,该方法有个缺点是由于委派者都是第一台主机,对应的facts变量都是该委派者的,所以没法使用其他主机的内置变量
# 故案例中也是使用register+shell来获取的主机名,而不是{{ inventory_hostname }}
 
# 执行结果:
$ ansible-playbook rolling.yml
 
PLAY [public] ************************************************************************************************************************************************************
 
TASK [Gathering Facts] ***************************************************************************************************************************************************
ok: [10.18.16.167]
ok: [10.18.16.166]
ok: [10.18.16.168]
ok: [10.18.12.213]
 
TASK [debug] *************************************************************************************************************************************************************
ok: [10.18.12.213] => {
    "msg": "I'm the Top public task"
}
ok: [10.18.16.167] => {
    "msg": "I'm the Top public task"
}
ok: [10.18.16.166] => {
    "msg": "I'm the Top public task"
}
ok: [10.18.16.168] => {
    "msg": "I'm the Top public task"
}
 
TASK [include_tasks] *****************************************************************************************************************************************************
included: /tmp/other.yml for 10.18.12.213
included: /tmp/other.yml for 10.18.12.213
included: /tmp/other.yml for 10.18.12.213
included: /tmp/other.yml for 10.18.12.213
 
TASK [shell] *************************************************************************************************************************************************************
changed: [10.18.12.213 -> 10.18.12.213]
 
TASK [debug] *************************************************************************************************************************************************************
ok: [10.18.12.213 -> 10.18.12.213] => {
    "msg": "I'm 10.18.12.213 "
}
 
TASK [shell] *************************************************************************************************************************************************************
changed: [10.18.12.213 -> 10.18.16.166]
 
TASK [debug] *************************************************************************************************************************************************************
ok: [10.18.12.213 -> 10.18.16.166] => {
    "msg": "I'm 10.18.16.166 "
}
 
TASK [shell] *************************************************************************************************************************************************************
changed: [10.18.12.213 -> 10.18.16.167]
 
TASK [debug] *************************************************************************************************************************************************************
ok: [10.18.12.213 -> 10.18.16.167] => {
    "msg": "I'm 10.18.16.167 "
}
 
TASK [shell] *************************************************************************************************************************************************************
changed: [10.18.12.213 -> 10.18.16.168]
 
TASK [debug] *************************************************************************************************************************************************************
ok: [10.18.12.213 -> 10.18.16.168] => {
    "msg": "I'm 10.18.16.168 "
}
 
PLAY RECAP ***************************************************************************************************************************************************************
10.18.12.213               : ok=14   changed=4    unreachable=0    failed=0   
10.18.16.166               : ok=2    changed=0    unreachable=0    failed=0   
10.18.16.167               : ok=2    changed=0    unreachable=0    failed=0   
10.18.16.168               : ok=2    changed=0    unreachable=0    failed=0 

15.在when里使用jinja2表达式

# 在when里是可以使用jinja2表达式的,只要格式规范即可。
# 一行表达式:
---
- hosts: target_host
  
  tasks:
  - name: Show debug
    debug: msg='条件满足你就会看到我。'
    when: '{% if def_a_var|d("abc") == "abc" %}{{ foo | d("true") }}{% endif %}'
 
# 等价的展开表达式
---
- hosts: target_host
  
  tasks:
  - name: Show debug
    debug: msg='条件满足你就会看到我。'
    when: |-
      {%- if def_a_var|d("abc") == "abc" -%}
      {{ foo | d("true") }}
      {%- endif -%}
 
# 执行(会有警告提示):
ansible-playbook test.yml 
 
PLAY [localhost] ********************************************************************************************************************************************
 
TASK [Gathering Facts] **************************************************************************************************************************************
ok: [localhost]
 
TASK [Show debug] *******************************************************************************************************************************************
 [WARNING]: when statements should not include jinja2 templating delimiters such as {{ }} or {% %}. Found: {% if def_a_var|d("abc") == "abc" %}{{ foo | d("true") }}{% endif %}
 
ok: [localhost] => {
    "msg": "条件满足你就会看到我。"
}
 
PLAY RECAP **************************************************************************************************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0

16.变量使用锚定和别名

# 通常我们在定义多个类似变量时,不同变量有定义相同的内容
# 比如:
---
- hosts: localhost
  vars:
    project_1:
      port: 8080
      jvm: '-Xms1G -Xmx1G'
      run_user: devops
      root: /data/project_1
    project_2:
      port: 8080
      jvm: '-Xms1G -Xmx1G'
      run_user: devops
      root: /data/project_2
 
  tasks:
 
   - debug: var=project_1
   - debug: var=project_2
   
# 可以看到`project_2`中的很多键值都跟`project_1`中是一样的,
# 那么我们有什么办法简化呢?
# 对此,yaml中提供了'&'来描定一个变量,并给描定的变量设置一个别名,
# 其他变量要引用锚定变量的值,需要再使用'<<'来引用并用*指向设置的别名
# 另外,具有相同键的变量会被后者覆盖
# 即:project_2的root会覆盖引用project_1的root
# 修改如下:
---
- hosts: localhost
  vars:
    project_1: &conf_anchor
      port: 8080
      jvm: '-Xms1G -Xmx1G'
      run_user: devops
      root: /data/project_1
    project_2:
      <<: *conf_anchor
      root: /data/project_2
 
  tasks:
 
   - debug: var=project_1
   - debug: var=project_2
# 其中:
# & 为锚定标识
# conf_anchor为设置的别名
# << 为合并键
# * 为引用符
# 执行如下(变量覆盖会有警告):
$ ansible-playbook anchor.yml 
 [WARNING]: While constructing a mapping from /tmp/anchor.yml, line 10, column 7, found a duplicate dict key (root). Using last defined value only.
 
PLAY [localhost] ********************************************************************************************************************************************
 
TASK [Gathering Facts] **************************************************************************************************************************************
ok: [localhost]
 
TASK [debug] ************************************************************************************************************************************************
ok: [localhost] => {
    "project_1": {
        "jvm": "-Xms1G -Xmx1G", 
        "port": 8080, 
        "root": "/data/project_1", 
        "run_user": "devops"
    }
}
 
TASK [debug] ************************************************************************************************************************************************
ok: [localhost] => {
    "project_2": {
        "jvm": "-Xms1G -Xmx1G", 
        "port": 8080, 
        "root": "/data/project_2", 
        "run_user": "devops"
    }
}
 
PLAY RECAP **************************************************************************************************************************************************
localhost                  : ok=3    changed=0    unreachable=0    failed=0
 
# 其他示例
 
# 引用锚定变量
---
- hosts: localhost
  vars:
    app:
      version: &my_version 1.1.1
      name:
        - "app_name"
        - *my_version
  tasks:
    debug:
        msg: app version is "{{ app.name | join('-') }}".
# 执行
$ ansible-playbook app.yml 
 
PLAY [localhost] *******************************************************************************************************************************************
 
TASK [Gathering Facts] *************************************************************************************************************************************
ok: [localhost]
 
TASK [debug] ***********************************************************************************************************************************************
ok: [localhost] => {
    "msg": "app version is \"app_name-1.1.1\"."
}
 
PLAY RECAP *************************************************************************************************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0
 
# 合并多个字典
---
- hosts: localhost
  vars:
    center: &C
      x: 1
      y: 2
    round: &R
      r: 3.1415
    big:
      <<: [*C,*R]
      g: 3x10e
  tasks:
  - debug: var=big
  
# 执行:
ansible-playbook muti.yml 
 
PLAY [localhost] *******************************************************************************************************************************************
 
TASK [Gathering Facts] *************************************************************************************************************************************
ok: [localhost]
 
TASK [debug] ***********************************************************************************************************************************************
ok: [localhost] => {
    "big": {
        "g": "3x10e", 
        "r": 3.1415, 
        "x": 1, 
        "y": 2
    }
}
 
PLAY RECAP *************************************************************************************************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0

17.定义带空格的key

# yaml是直接可以定义带空格的变量的
# 或者你可以用?:的方式
# 两者完全等价的。
- hosts: localhost
  vars:
    my_vars:
    # 直接定义
      'this is a key1': 'this is a value1'
    # 或者使用?:的方式
      ? 'this is a key2'
      : 'this is a value2'
  tasks:
  - debug: var=my_vars
 
# 执行 
$ ansible-playbook space_var.yml 
 
PLAY [localhost] **************************************************************************************************************************************************************
 
TASK [Gathering Facts] ********************************************************************************************************************************************************
ok: [localhost]
 
TASK [debug] ******************************************************************************************************************************************************************
ok: [localhost] => {
    "my_vars": {
        "this is a key1": "this is a value1", 
        "this is a key2": "this is a value2"
    }
}
 
PLAY RECAP ********************************************************************************************************************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0

18.定义变量时,设置类型

# 其实在定义变量时,我们是可以设置类型的。
# 当然,你在加引号与不加引号时就能解决大多的定义问题
- hosts: localhost
  vars:
    type_vars:
      is_str: !!str 9111
      is_int: !!int '8081'
      is_float: !!float 2019
      is_bool: !!bool 'yes'
 
  tasks:
  - debug: var=type_vars
 
# 执行:
$ ansible-playbook type_vars.yml 
 
PLAY [localhost] **************************************************************************************************************************************************************
 
TASK [Gathering Facts] ********************************************************************************************************************************************************
ok: [localhost]
 
TASK [debug] ******************************************************************************************************************************************************************
ok: [localhost] => {
    "type_vars": {
        "is_bool": true, 
        "is_float": 2019.0, 
        "is_int": 8081, 
        "is_str": "9111"
    }
}
 
PLAY RECAP ********************************************************************************************************************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0

19.数字精度

# 利用format可以保留精度
$ ansible localhost -m debug -a "msg={{ '%0.2f -- %0.3f -- %d'| format(123,2.7,4.44) }}"
localhost | SUCCESS => {
    "msg": "123.00 -- 2.700 -- 4"
}

20.循环(多实例)任务触发handlers重启任务

# 我们在使用ansible部署多实例任务时
# 多个实例完成后需要触发handler任务来重启各个实例(实例名称通常是动态/不固定的),
# 而通常实例的服务名称是有区别的,而且是按需重启(满足状态变化),逐个重启
# 我们可以借助json_query的filter特性来获取changed状态满足条件的实例来循环重启
# 如,实例变量如下:
---
  vars:
    redis_ins_port:
      - 6379
      - 6380
      - 6381
# 我们要循环复制实例模板到客户端,触发handler后只重启发生变化的实例
# 复制模板并将任务状态注册到变量
- name: 
  template: src=redis.conf.j2 dest=/etc/redis/redis_{{ server_port }}.conf"
  loop_control:
    loop_var: server_port
  loop: "{{ redis_ins_port }}"
  register: task_status
  notify:
  - Restart redis
  
# handler任务
# 其中,json_query为查询task_status中changed的状态为true的所有server_port
---
- name: Restart redis
  systemd: name=redis_{{ item }} state=restarted" 
  loop: "{{ task_status | json_query('results[?changed].server_port') }}

21.修改列表内的所有字段值,生成新的列表

# 我们在创建例如mongodb replicasets时,使用模块示例如下:
# Create a replicaset called 'rs0' with the 3 provided members
- name: Ensure replicaset rs0 exists
  mongodb_replicaset:
    login_host: localhost
    login_user: admin
    login_password: admin
    replica_set: rs0
    members:
    - mongodb1:27017
    - mongodb2:27017
    - mongodb3:27017
  when:ansible_play_hosts.index(inventory_hostname) == 0
  
# 对于其中的member如何能不固定通过方法自动创建呢?
# 试想,我们已经有了执行的主机列表ansible_play_hosts
# (虽然里面的值可能是别名,即:inventory_hostname)
# 但是这个列表么有对应的端口,即我们需要修改列表里的每一项,添加一个端口即可
# 像这样:['10.18.1.190:27017','10.18.1.191:27017','10.18.1.192:27017']
# 改动如下:
- name: Ensure replicaset rs0 exists
  mongodb_replicaset:
    login_host: localhost
    login_user: admin
    login_password: admin
    replica_set: rs0
    members: >-
      {{ anible_play_hosts | map('extract', hostvars, 'ansible_default_ipv4') | list | json_query('[*].address') |map('regex_replace','(.*)','\\1:' + '27017' | list }}
  when: ansible_play_hosts.index(inventory_hostname) == 0
# 其中:
# ansible_play_hosts为当前执行的所有主机列表
# 第一个map extract为提取hostvars中每个ansible_play_hosts的值作为键,并取ansible_default_ipv4,就像:hostvars[inventory_hostname]['ansible_default_ipv4']
# list把map对象转换成列表
# json_query把列表内每个address提取出来变成新的列表,到此是为了提取IP地址列表,当然,你ansible_play_hosts是IP地址的话可以不用这么麻烦
# 第二个map regex_replace把每个IP地址变成IP+:27017,
# 最后再把我们的map对象转成list,得到我们想要的:['10.18.1.190:27017','10.18.1.191:27017','10.18.1.192:27017']

22.待续…

如果你有更好的姿势跟建议,欢迎补充

0 条回应