06Ansible 事实、循坏、条件判断

1.管理事实

1.1Ansible事实

是Ansible在受管主机上自动检测到的变量。事实中包含有与主机相关的信息,可以像play中的常规变量、条件、循环或依赖于从受管主机收集的值的任何其他语句那样使用。

为受管主机收集的一些事实可能包括:

  • 主机名称
  • 内核版本
  • 网络接口
  • IP地址
  • 操作系统版本
  • 各种环境变量
  • CPU数量
  • 提供的或可用的内存
  • 可用磁盘空间

借助事实,可以方便地检索受管主机的状态,并根据该状态确定要执行的操作。例如:

  • 可以根据含有受管主机当前内核版本的事实运行条件任务,以此来重启服务器
  • 可以根据通过事实报告的可用内存来自定义MySQL配置文件
  • 可以根据事实的值设置配置文件中使用的IPv4地址

通常,每个play在执行第一个任务之前会先自动运行setup模块来收集事实。

查看为受管主机收集的事实的一种方式是,运行一个收集事实并使用debug模块显示ansible_facts变量值的简短playbook。

基于模块获取事实

[root@master project]# ansible all -m setup      //获取受控机事实
   [root@master project]# ansible all -m setup | less   //这条命令可以看到事实的前面
   

基于playbook获取事实

---
- hosts: 192.168.8.132
  tasks:
    - name: test
      debug:
        var: ansible_facts['default_ipv4']['address']   //获取默认ipv4address事实

[root@master project]# ansible-playbook playbook/test.yml 

PLAY [192.168.8.132] ***************************************************************************

TASK [Gathering Facts] *************************************************************************
ok: [192.168.8.132]

TASK [test] ************************************************************************************
ok: [192.168.8.132] => {
    "ansible_facts['default_ipv4']['address']": "192.168.8.132"
}

PLAY RECAP *************************************************************************************
192.168.8.132              : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@master project]# 

下表显示了可能从受管节点收集的并可在playbook中使用的一些事实:
Ansible事实的示例

事实变量
短主机名ansible_facts[‘hostname’]
完全限定域名ansible_facts[‘fqdn’]
IPv4地址ansible_facts[‘default_ipv4’][‘address’]
所有网络接口的名称列表ansible_facts[‘interfaces’]
/dev/vda1磁盘分区的大小ansible_facts[‘devices’][‘vda’][‘partitions’][‘vda1’][‘size’]
DNS服务器列表ansible_facts[‘dns’][‘nameservers’]
当前运行的内核版本ansible_facts[‘kernel’]

如果变量的值为散列/字典类型,则可使用两种语法来获取其值。比如:

  • ansible_facts['default_ipv4']['address']也可以写成ansible_facts.default_ipv4.address
  • ansible_facts['dns']['nameservers']也可以写成ansible_facts.dns.nameservers

在play中使用事实时,Ansible将事实的变量名动态替换为对应的值:

//获取主机名为fqdn的默认ipvd地址
---
- hosts: 192.168.8.132
  tasks:
    - name: test
      debug:
        msg: >
          The host named {{ ansible_facts['fqdn'] }} of
          ip  is {{ ansible_facts['default_ipv4']['address'] }} 

[root@master project]# ansible-playbook playbook/test.yml 

PLAY [192.168.8.132] ***************************************************************************

TASK [Gathering Facts] *************************************************************************
ok: [192.168.8.132]

TASK [test] ************************************************************************************
ok: [192.168.8.132] => {
    "msg": "The host named localhost.localdomain of ip  is 192.168.8.132 \n"
}

PLAY RECAP *************************************************************************************
192.168.8.132              : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

将Ansible配置文件的[default]部分中inject_facts_as_vars参数设置为False,可关闭旧命名系统。默认设置目前为True。

# inject_facts_as_vars = True

1.2 关闭事实收集

有时我们不想为play收集事实。这样做的原因可能有:

  • 不准备使用任何事实
  • 希望加快play速度
  • 希望减小play在受管主机上造成的负载
  • 受管主机因为某种原因无法运行setup模块
  • 需要安装一些必备软件后再收集事实

以上种种原因导致我们可能想要永久或暂时关闭事实收集的功能,要为play禁用事实收集功能,可将gather_facts关键字设置为no:

---
- name: gather_facts
  hosts: 192.168.8.132
  gather_facts: no
  tasks:
    - name: get gather_facts
      setup:
    - name: debug
      debug:
        var: ansible_facts

1.3 创建自定义事实

除了使用系统捕获的事实外,我们还可以自定义事实,并将其本地存储在每个受管主机上。这些事实整合到setup模块在受管主机上运行时收集的标准事实列表中。它们让受管主机能够向Ansible提供任意变量,以用于调整play的行为。

自定义事实可以在静态文件中定义,格式可为INI文件或采用JSON。它们也可以是生成JSON输出的可执行脚本,如同动态清单脚本一样。

有了自定义事实,我们可以为受管主机定义特定的值,供play用于填充配置文件或有条件地运行任务。动态自定义事实允许在play运行时以编程方式确定这些事实的值,甚至还可以确定提供哪些事实。

默认情况下,setup模块从各受管主机的/etc/ansible/facts.d目录下的文件和脚本中加载自定义事实。各个文件或脚本的名称必须以.fact结尾才能被使用。动态自定义事实脚本必须输出JSON格式的事实,而且必须是可执行文件。

以下是采用INI格式编写的静态自定义事实文件。INI格式的自定义事实文件包含由一个部分定义的顶层值,后跟用于待定义的事实的键值对:

//在受控机上创建自定义事实
[root@localhost ~]# mkdir -p /etc/ansible/facts.d
[root@localhost ~]# cd /etc/ansible/facts.d/
[root@localhost facts.d]# vim test.fact
[root@localhost facts.d]# cat test.fact
[ckages]
web_package = httpd
db_package = mariadb-server

[users]
user1 = joe
user2 = janepackages]

[root@localhost facts.d]# 

//在主机上去验证效果

...skipping...
        "ansible_local": {
            "test": {
                "ckages": {
                    "db_package": "mariadb-server",
                    "web_package": "httpd"
                },
                "users": {
                    "user1": "joe",
                    "user2": "janepackages]"
                }
            }
        },
        "ansible_lsb": {},
        "ansible_lvm": {

2.编写循坏和条件任务

2.1利用循环迭代任务

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

Ansible支持使用loop关键字对一组项目迭代任务。可以配置循环以利用列表中的各个项目、列表中各个文件的内容、生成的数字序列或更为复杂的结构来重复任务。

2.2简单循坏

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

请思考以下代码片段,它使用三次user模块来创建三个用户:

- hosts: 192.168.8.132
  gather_facts: no
  tasks:
    - name: create user
      user:
        name: pyd1
        state: present
        
    - name: create user
      user:
        name: pyd2
        state: present
        
     - name: create user
       user:
         name: pyd3
         state: present
        

这两个任务可以重新编写为使用一个简单循环,创建三个用户;

---
- hosts: 192.168.8.132
  gather_facts: no
  tasks:
    - name: create user
      user:
        name: '{{ item  }}'
        state: present
      loop:
        - pyd1
        - pyd2
        - pyd3

[root@localhost ~]# id pyd1
uid=1002(pyd1) gid=1002(pyd1)=1002(pyd1)
[root@localhost ~]# id pyd2
uid=1003(pyd2) gid=1003(pyd2)=1003(pyd2)
[root@localhost ~]# id pyd3
uid=1004(pyd3) gid=1004(pyd3)=1004(pyd3)
[root@localhost ~]# 

可以通过一个变量提供loop所使用的列表。在以下示例中,变量vars/users.yml含有需要处于运行状态的服务的列表。

---
- hosts: 192.168.8.132
  gather_facts: no
  vars_files:
    - vars/users.yml
  tasks:
    - name: create user
      user:
        name: '{{ item  }}'
        state: present
      loop: "{{ users  }}"
[root@master project]# cat playbook/vars/users.yml 
users:
  - pyd4
  - pyd5
  - pyd6
[root@master project]# 

[root@localhost ~]# id pyd4
uid=1005(pyd4) gid=1005(pyd4)=1005(pyd4)
[root@localhost ~]# id pyd5
uid=1006(pyd5) gid=1006(pyd5)=1006(pyd5)
[root@localhost ~]# id pyd6
uid=1007(pyd6) gid=1007(pyd6)=1007(pyd6)
[root@localhost ~]# 

2 .3循环散列或字典列表

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

---
- hosts: 192.168.8.132
  gather_facts: no
  vars_files:
    - vars/users.yml
  tasks:
    - name: create user
      user:
        name: '{{ item.name }}'
        uid: '{{ item.uid }}'
        state: present
      loop: "{{ users }}"
[root@master project]# cat playbook/vars/users.yml 
users:
  - name: pyd8
    uid: 2200
  - name: pyd9
    uid: 2500
  - name: pyd10
    uid: 3000
[root@master project]# 

[root@localhost ~]# id pyd8
uid=2200(pyd8) gid=2200(pyd8)=2200(pyd8)
[root@localhost ~]# id pyd9
uid=2500(pyd9) gid=1500(pyd9)=1500(pyd9)
[root@localhost ~]# id pyd10
uid=3000(pyd10) gid=2000(pyd10)=2000(pyd10)
[root@localhost ~]# 

2.4较早样式的循坏关键字

在Ansible2.5之前,大多数playbook使用不同的循环语法。提供了多个循环关键字,前缀为whth_,后面跟Ansible查找插件的名称。这种循环语法在现有playbook中很常见,但在将来的某个时候可能会被弃用。

较早样式的Ansible循环

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

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

---
- hosts: 192.168.8.132
  gather_facts: no
  vars_files:
    - vars/users.yml
  tasks:
    - name: create user
      user:
        name: '{{ item.name }}'
        uid: '{{ item.uid }}'
        state: present
      with_items: "{{ users }}"

从Ansible2.5开始,建议使用loop关键字编写循环。

2.5将Register变量与Loop一起使用

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

---
- hosts: 192.168.8.132
  gather_facts: no
  tasks:
    - name: test
      command: 'echo hello {{ item }},how are you.'
      loop:
        - tom
        - Bob
        - alice
      register: result         //注册echo_item变量

    - debug:
        var: result            //echo_item变量的内容显示在屏幕上

ok: [192.168.8.132] => {
    "result": {
        "changed": true,
        "msg": "All items completed",
        "results": [
            {
                "ansible_facts": {
                    "discovered_interpreter_python": "/usr/libexec/platform-python"
                },
                "ansible_loop_var": "item",
                "changed": true,
                "cmd": [
                    "echo",
                    "hello",
                    "tom,how",
                    "are",
                    "you."

2.6有条件地运行任务

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

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

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

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

2.6.1条件任务语法

when语句用于有条件地运行任务。它取要测试的条件为值。如果条件满足,则运行任务。如果条件不满足,则跳过任务。

可以测试的一个最简单条件是某一布尔变量是True还是False。以下示例中的when语句导致任务仅在power为`True时运行:

---
- hosts: 192.168.8.132
  gather_facts: no
  vars_files:
    - vars/users.yml
  vars:
    power: False
  tasks:
    - name: create user
      user:
        name: '{{ item.name }}'
        uid: '{{ item.uid }}'
        state: present
      loop: '{{ users }}'
      when: power

[root@master project]# ansible-playbook playbook/usrs.yml 

PLAY [192.168.8.132] ***************************************************************************

TASK [create user] *****************************************************************************
skipping: [192.168.8.132] => (item={'name': 'pyd8', 'uid': 2200}) 
skipping: [192.168.8.132] => (item={'name': 'pyd9', 'uid': 2500}) 
skipping: [192.168.8.132] => (item={'name': 'pyd10', 'uid': 3000}) 

PLAY RECAP *************************************************************************************
192.168.8.132              : ok=0    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0   

---
- hosts: 192.168.8.132
  gather_facts: no
  vars_files:
    - vars/users.yml
  vars:
    power: True
  tasks:
    - name: create user
      user:
        name: '{{ item.name }}'
        uid: '{{ item.uid }}'
        state: absent 
      loop: '{{ users }}'
      when: power

[root@master project]# ansible-playbook playbook/usrs.yml 

PLAY [192.168.8.132] ***************************************************************************

TASK [create user] *****************************************************************************
changed: [192.168.8.132] => (item={'name': 'pyd8', 'uid': 2200})
changed: [192.168.8.132] => (item={'name': 'pyd9', 'uid': 2500})
changed: [192.168.8.132] => (item={'name': 'pyd10', 'uid': 3000})

PLAY RECAP *************************************************************************************
192.168.8.132              : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

---
- hosts: 192.168.8.132
  gather_facts: no
  vars_files:
    - vars/users.yml
  vars:
    power: True
  tasks:
    - name: create user
      user:
        name: '{{ item.name }}'
        uid: '{{ item.uid }}'
        state: present
      loop: '{{ users }}'
      when: '"{{ item.name }}" == "pyd7"'            //当条件为pyd8时我就执行这条命令,如果不是我就直接跳过
[root@master project]# ansible-playbook playbook/usrs.yml 

PLAY [192.168.8.132] ***************************************************************************

TASK [create user] *****************************************************************************
[WARNING]: conditional statements should not include jinja2 templating delimiters such as {{ }}
or {% %}. Found: "{{ item.name }}" == "pyd8"
changed: [192.168.8.132] => (item={'name': 'pyd8', 'uid': 2200})
skipping: [192.168.8.132] => (item={'name': 'pyd9', 'uid': 2500}) 
skipping: [192.168.8.132] => (item={'name': 'pyd10', 'uid': 3000}) 

PLAY RECAP *************************************************************************************
192.168.8.132              : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@master project]# 

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

---
- name: test httpd is Defined Demo
  hosts: httpd
  vars:
    my_service: httpd
  tasks:
    - name: '{{ my_service }} package is installed'
      dnf:
        name: '{{ my_service }}'
      when: my_service is defined

[root@master project]# ansible-playbook playbook/test.yml 

PLAY [test httpd is Defined Demo] **************************************************************

TASK [Gathering Facts] *************************************************************************
ok: [192.168.8.132]

TASK [httpd package is installed] **************************************************************
changed: [192.168.8.132]

PLAY RECAP *************************************************************************************
192.168.8.132              : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

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

操作示例
等于(值为字符串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。1、True或yes的求值为Truememory_available
布尔变量是False。0、False或no的求值为Falsenot memory_available
第一个变量的值存在,作为第二个变量的列表中的值ansible_distribution in supported_distros
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

彭宇栋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值