Ansible的循环控制及条件任务 Ansible Playbook的循环控制、条件控制、处理程序以及错误处理机制。
loop循环控制 Ansible支持使用loop关键字来执行循环任务。
简单迭代任务循环 将loop关键字添加到任务中,并用列表表示要迭代的项目的值。并使用item临时变量来保存每次循环迭代过程中使用的值。
如下是一个启动两个服务的例子,在没有了解循环控制时应这样编写playbook,分别写两个独立的任务来执行:
1 2 3 4 5 6 7 8 9 - name: Postfix is running service: name: postfix state: started - name: httpd is running service: name: httpd state: started
使用loop循环控制,则可以简写如下:
1 2 3 4 5 6 7 - name : Start Postfix and httpd services service: name: "{{ item }} " state: started loop: - postfix - httpd
也可以把loop所使用的列表存到play的变量中,他们三个执行的效果是相同的:
1 2 3 4 5 6 7 8 9 10 11 vars: start_services: - postfix - httpd tasks: - name : Start Postfix and httpd services service: name: "{{ item }} " state: started loop: "{{ start_services }} "
循环散列或字典列表 loop也可以循环散列或者是字典,下面示例中每个字典或散列有两个键,分别是name和groups。当前循环中的每个键的值可以分别通过item.name和item.group来检索。
1 2 3 4 5 6 7 8 9 10 - name: User exist and are in the corrent Groups user: name: "{{ item.name }} " group: "{{ item.group }} " loop: - name: user1 group: wheel - name: user2 group: root
早期的循环语法 在Ansible 2.5之前,大多数的playbook使用不同的循环愈发。提供了多个循环关键字,都以前缀with_
开头,后跟Ansible查找插件的名称连用。这种循环语法在目前依旧很常见,但在未来某个时刻将会被弃用。
循环关键字
描述
with_items
和loop类似,但当为with_items提供了列表的列表,他们会被扁平化处理为单级列表。item作为循环变量保存每次迭代过程中的值
with_file
此关键字需要控制节点文件名列表。循环变量item保存每次迭代过程中保存文件列表中相应文件的内容
wirh_sequence
此关键字不需要列表,而是需要参数生成数字序列列表。循环变量item在每次迭代过程中保存生成的序列中的一个生成项的值
将Register变量与Loop一起使用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 --- - name: Loop Register Test gather_facts: no hosts: localhost tasks: - name: Loop Echo Task shell: "echo This is my item: {{ item }} " loop: - one - two register: echo_results - name: Show echo_results variable debug: var: echo_results
条件任务语句 使用条件语句when
可以控制该任务是否执行,根据条件配置满足一定情况下执行任务。比如在执行任务前首先要判断一下磁盘剩余空间、内存大小是否满足需求。如果不满足则直接跳过该任务。
变量也可以作为when条件语句的判断条件,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 --- - name: Test Boolean Task Demo hosts: all vars: run_task: true tasks: - name: Install the web services yum: name: httpd state: present when: run_task ...
在当变量run_task
为真的时候,则执行Install the web services
的任务,否则跳过该任务。
下面是常用的判断条件列表。
判断条件
示例
等于(字符串)
ansible_machine == “x86_64”
等于(数字)
max_memory == 512
常见判断
<=、<、>、>=、!=
变量存在
max_memory is defined
变量不存在
max_memory is not defined
第一个变量在第二个变量的列表里
var1 in var_all
多个条件组合 在使用when
条件语句时,如果要判断组合条件,可以使用and
、or
关键字来进行组合,并与括号分组条件。
when: ansible_facts.distribution == "RedHat" or ansible_facts.distribution == "Debian"
如果多个条件之间是and关系,也可以使用列表的形式来表示:
1 2 3 when: - ansible_facts.distribution == "RedHat" - ansible_facts.distribution == "CentOS"
使用括号能编写更复杂的条件:
1 2 3 4 when: > (ansible_facts.distribution == "RedHat" and ansible_facts.distribution_major_version == "7") or (ansible_facts.distribution == "Fedora" and ansible_facts.distribution_major_version == "28")
loop循环和条件判断组合使用 例子中yum模块将要安装mariadb-server软件包,但是要求根目录满足剩余空间300MB以上才会安装,所以可以遍历目标主机上所有挂在的磁盘然后找到根目录再判断剩余空间,如果满足则安装,不满足则跳过该任务。
当对某个任务结合使用when和loop时,将对每一项都使用when语句进行判断。
1 2 3 4 5 6 7 tasks: - name: Make Sure root have enough space to install mariadb yum: name: mariadb-server state: present loop: "{{ ansible_facts.mounts }} " when: item.mount == "/" and item.size_available > 300000000
处理程序 有时候我们需要更改完配置文件后重启服务,但只想对文件有更改的情况下才重启服务。得益于Ansible的模块设计的幂等性,我们可以通过判断是否进行了更改而选择执行任务。这种方式叫处理程序。
处理程序可看作非活动任务,只有在使用notify语句激活后才会被触发,只有配置文件更新了并激活了该处理任务时,在到处理程序定义的位置时才会执行该任务。
处理任务只有在被激活的情况下才会执行,要注意的是处理程序不会按照你的激活顺序执行,而是按照激活程序的编写顺序以此判断是否被激活和执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 - name: Enable internet services hosts: all become: yes tasks: - name: Add Web content get_url: url: http://materials.example.com/labs/playbook-review/index.php dest: /var/www/html/index.php mode: 0644 notify: restart apache handlers: - name: restart apache systemd: name: httpd state: restarted ...
在第一次添加新页面后,激活restart apache
处理程序,当开始执行handlers处理程序时,发现restart apache
处理程序被激活,则执行该任务。
如果第二次执行该playbook你会发现处理程序并没有执行,因为任务Add Web content
返回的状态是OK,由于没有进行任何更改所以不会执行重启httpd服务的操作。
如何处理任务失败 Ansible在执行任务的过程中,有任何一个任务执行失败,则不论接下来是否还有任务都不会执行。
但是如果你有特定的要求,即便是某一个任务执行失败,也继续往下执行其他任务的话,你需要在对应的任务上添加ignore_errors
关键字。
1 2 3 4 5 - name: Latest version of notapkg is installed yum: name: notapkg state: latest ignore_errors: yes
任务失败后强制执行处理程序 哪怕是Play在执行任务的过程中失败了,也会强制执行已经被激活的处理程序。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 --- - name: Enable internet services force_handlers: yes hosts: all become: yes tasks: - name: Latest version of notapkg is installed yum: name: httpd state: latest notify: - restart apache - name: Always is False command: /bin/false handlers: - name: restart apache systemd: name: httpd state: restarted ...
指定任务失败的条件 使用failed_when
关键字可以指定任务已失败的条件,条件语句和when的条件语句相同。
下面的例子是当有任何一个磁盘挂载点剩余的空间小于300MB则将任务结果返回为失败,即便是已经成功安装。
1 2 3 4 5 6 7 tasks: - name: Make Sure root have enough space to install mariadb yum: name: mariadb-server state: present loop: "{{ ansible_facts.mounts }} " failed_when: item.size_available < 300000000
如果failed_when
关键字为true
那么不管最后的执行结果如何,都会返回任务处理结果为失败。
指定是否报告任务的Changed结果 当任务对托管主机进行了更改后,如果有处理任务,则会激活处理任务。但在有些特殊模块如command模块,有时并不能按照预期来返回ok结果,所以只能返回Changed作为任务执行结果,这将会激活处理任务。
使用changed_when
关键字可以控制何时返回Changed执行结果。他和failed_when
关键字一样,和when
的条件语句相同。你可以对某一变量是否在另一个变量中出现过进行判断。
1 2 3 4 5 6 7 8 9 10 tasks: - shell: cmd: /usr/local/bin/upgrade-database register: command_result changed_when: "'Success' in command_result.stdout" handelers: - name: restart database service: - name: mariadb state: restarted
Ansible 块和错误自动处理 在Playbook中,块用来对任务进行逻辑分组,可用于控制任务的执行方式,例如任务块可以和when
关键字连用,可以将某一条件用于多个任务。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 --- - name: Enable internet services hosts: all become: yes tasks: - name: Installed Service block: - name: Latest version of notapkg is installed yum: name: httpd state: latest - name: Always is True command: /bin/true changed_when: false when: ansible_facts.distribution == "RedHat" - name: Always is True command: /bin/true changed_when: false ...
通过块,也可以与rescue和always语句连用来处理错误。如果块中包裹的任务有任何一个执行失败,则执行其rescue块中的任务来进行恢复。在block子句中的任务以及rescue运行结束后,最后运行always语句中包含的任务。
block:定义要运行的主要任务
rescue:定义在block块中有任务执行失败后要运行的任务
always:始终都要执行的任务,不论block和rescue子句中定义的任务执行成功还是失败都会运行always中定义的任务。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 --- - name: Test Block hosts: all become: yes tasks: - name: Installed Service block: - name: Latest version of notapkg is installed yum: name: httpd state: latest - name: Always is True command: /bin/true changed_when: false when: ansible_facts.distribution == "RedHat" rescue: - name: rescue block tasks debug: msg: "rescued block tasks" always: - name: Always has been exec debug: msg: "Always has been exec" ...
任务控制总结
vars变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 services: - "{{ web_service }} " - "{{ fw_service }} " packages: - "{{ web_package }} " - "{{ ssl_package }} " - "{{ fw_package }} " ssl_cert_dir: /etc/httpd/conf.d/ssl web_config_files: - src: server.key dest: "{{ ssl_cert_dir }} " - src: server.crt dest: "{{ ssl_cert_dir }} " - src: ssl.conf dest: /etc/httpd/conf.d - src: index.html dest: /var/www/html
PlayBook
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 - name: Playbook Control Lab hosts: webservers vars_files: vars.yml tasks: - name: check ram size fail: msg: "Cant install under free ram 256M" when: ansible_facts.memtotal_mb < min_ram_mb and ansible_facts.distribution != "RedHat" - name: Installed "{{ packages }} " Packages yum: name: "{{ packages }} " state: present - name: Enable and start "{{ services }} " services service: name: "{{ item }} " state: started enabled: true loop: "{{ services }} " - name: Config Document Tasks block: - name: existed ssl_cret_dir file: path: "{{ ssl_cert_dir }} " state: directory - name: Config File existed copy: src: "{{ item.src }} " dest: "{{ item.dest }} " loop: "{{ web_config_files }} " notify: - restart web service rescue: - debug: msg: > One or more of the configuration changes faild, but the web service is still active. - name: Configed Firewalld Allowed firewalld: service: "{{ item }} " state: enabled permanent: true immediate: true loop: - http - https handlers: - name: restart web service service: name: "{{ web_service }} " state: restarted