Ansible-Playbook的任务控制
文章目录
一、任务控制基本介绍
这里主要来介绍PlayBook中的任务控制。
任务控制类似于编程语言中的if … 、for … 等逻辑控制语句
1、Playbook示例
在下面的PlayBook中,我们创建了 tomcat、www 和 mysql 三个用户。安装了Nginx 软件包、并同时更新了 Nginx 主配置文件和虚拟主机配置文件,最后让Nginx 服务处于启动状态。
---
- name: task control playbook example
hosts: webservers
tasks:
- name: create tomcat user
user: name=tomcat state=present
- name: create www user
user: name=www state=present
- name: create mysql user
user: name=mysql state=present
- name: yum nginx webserver
yum: name=nginx state=present
- name: update nginx main config
copy: src=nginx.conf dest=/etc/nginx/
- name: add virtualhost config
copy: src=www.qfedu.com.conf dest=/etc/nginx/conf.d/
- name: start nginx server
service: name=nginx state=started
下面针对上面的playbook的问题,进行优化
二、条件判断
问题一、Nginx启动逻辑欠缺考虑
启动Nginx之前需要进行配置文件语法检测
检验通过之后再进行启动Nginx
Nginx 语法校验
- name: check nginx syntax
shell: nginx -t
通过注册变量将Nginx检查语法的Tasks结果与Nginx的启动Tasks关联起来,根据这个结果去判断“启动NGINX的TASK”是否执行
获取Task任务结果
- name: check nginx syntax
shell: /usr/sbin/nginx -t
register: nginxsyntax
获取到任务结果,但是结果里面的内容是个什么样子, 我如何根据内容在后续的PlayBook中使用呢?
通过debug模块去确认返回结果的数据结构
- name: print nginx syntax result
debug: var=nginxsyntax
通过debug 模块,打印出来的返回结果。 当nginxsyntax.rc 为 0 时语法校验正确
用到的nginx配置文件
主配置文件
# cat nginx.conf
user www;
worker_processes 2;
error_log /var/log/nginx/error.log;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
sendfile on;
tcp_nopush on;
keepalive_timeout 0;
gzip on;
gzip_min_length 1k;
gzip_buffers 8 64k;
gzip_http_version 1.0;
gzip_comp_level 5;
gzip_types text/plain application/x-javascript text/css application/json application/xml application/x-shockwave-flash application/javascript image/svg+xml image/x-icon;
gzip_vary on;
include /etc/nginx/conf.d/*.conf;
}
nginx子配置文件
server {
listen 80;
server_name www.qfedu.com;
root /usr/share/nginx/html;
access_log /var/log/nginx/www.qfedu.com-access_log main;
error_log /var/log/nginx/www.qfedu.com-error_log;
add_header Access-Control-Allow-Origin *;
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ {
expires 1d;
}
location ~ .*\.(js|css)?$ {
expires 1d;
}
}
通过条件判断(when) 指令去使用语法校验的结果
- name: check nginx syntax
shell: /usr/sbin/nginx -t
register: nginxsyntax
- name: print nginx syntax
debug: var=nginxsyntax
- name: start nginx server
service: name=nginx state=started
when: nginxsyntax.rc == 0
改进后的playbook
---
- name: task control palybook examle
hosts: 10.11.67.32
gather_facts: no
remote_user: root
tasks:
- name: create tomcat user
user: name=tomcat state=present
- name: create www user
user: name=www state=present
- name: create mysql user
user: name=mysql state=present
- name: yum nginx webserver
yum: name=nginx state=present
- name: update nginx config
copy: src=nginx.conf dest=/etc/nginx/conf.d/
- name: check nginx config
shell: ls
register: nginxsyncheck
- name: print nginxsyncheck
debug: var=nginxsyncheck
- name: start nginx server
systemd: name=nginx state=started
when: nginxsyncheck.rc == 0
以上的逻辑,只要语法检查通过都会去执行 "start nginx server"这个TASK。在这个问题的解决里,我们学习了when 条件判断和注册变量的结合使用。学习了when条件判断中是可以支持复杂逻辑的。比如现在用到的逻辑运算符 and
==
!=
> >=
< <=
is defined
is not defined
true
false
支持逻辑运算符: and or
三、循环控制
三、循环控制
playbook也存在循环控制,我们也可以像写shell一样简单的去完成多用户创建工作,在PlayBook中使用 with_items 去实现循环控制,且循环时的中间变量循环变量只能是关键字 item,而不能随意自定义
with_items:实现循环控制
item:定义循环变量
使用定义了剧本变量 createuser(一个列表) ,然后通过 with_items 循环遍历变量这个变量来达到创建用户的目的。
- name: variable playbook example
hosts: webservers
gather_facts: no
vars:
createuser:
- tomcat
- www
- mysql
tasks:
- name: create user
user: name="{{ item }}" state=present
with_items: "{{ createuser }}"
# createuser 是被循环的对象
# item 每次循环被赋值的变量
- name: variable playbook example
hosts: webservers
gather_facts: no
vars:
createuser:
- tomcat
- www
- mysql
tasks:
- name: create user
user: name="{{ item }}" state=present
with_items: "{{ createuser }}"
# createuser 是被循环的对象
# item 每次循环被赋值的变量
- name: yum nginx webserver
yum: name=nginx state=present
- name: update nginx main config
copy: src=nginx.conf dest=/etc/nginx/
- name: add virtualhost config
copy: src=www.qfedu.com.conf dest=/etc/nginx/conf.d/
- name: check nginx syntax
shell: /usr/sbin/nginx -t
register: nginxsyntax
- name: print nginx syntax
debug: var=nginxsyntax
- name: start nginx server
service: name=nginx state=started
when: nginxsyntax.rc == 0
新版本的loop循环
- name: loop item
hosts: all
gather_facts: no
vars:
some_list:
- "a"
- "b"
- "c"
num_list:
- 1
- 2
- 3
- 5
tasks:
- name: show item
debug:
var: item
loop: "{{ some_list }}"
- name: show item when item > 3
debug:
var: item
loop: "{{ num_list }}"
when: item > 3
# 循环中还可以加判断,这里是当循环的数据是大于 3 的,
# 才赋值个 var ,从而打印出来
[kakaops@ka ansible]$ sudo ansible-playbook loop.yaml
[sudo] kakaops 的密码:
__________________
< PLAY [loop item] >
------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
_______________________
< TASK [show some_list] >
-----------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
Saturday 07 November 2020 09:14:32 +0800 (0:00:00.111) 0:00:00.112 *****
ok: [10.11.67.32] => (item=a) => {
"ansible_loop_var": "item",
"item": "a"
}
ok: [10.11.67.32] => (item=b) => {
"ansible_loop_var": "item",
"item": "b"
}
ok: [10.11.67.32] => (item=c) => {
"ansible_loop_var": "item",
"item": "c"
}
____________________________________
< TASK [show num_list when item > 3] >
------------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
Saturday 07 November 2020 09:14:32 +0800 (0:00:00.159) 0:00:00.271 *****
skipping: [10.11.67.32] => (item=1)
skipping: [10.11.67.32] => (item=2)
skipping: [10.11.67.32] => (item=3)
skipping: [10.11.67.32] => (item=4)
ok: [10.11.67.32] => (item=5) => {
"ansible_loop_var": "item",
"item": 5
}
ok: [10.11.67.32] => (item=6) => {
"ansible_loop_var": "item",
"item": 6
}
____________
< PLAY RECAP >
------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
10.11.67.32 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Saturday 07 November 2020 09:14:32 +0800 (0:00:00.168) 0:00:00.439 *****
===============================================================================
debug ------------------------------------------------------------------- 0.33s
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
total ------------------------------------------------------------------- 0.33s
Playbook run took 0 days, 0 hours, 0 minutes, 0 seconds
四、Tags属性
考虑这样一个情况:
若更新了Nginx 的配置文件后,我们需要通过PlayBook将新的配置发布到生产服务器上,然后再重新加载我们的Nginx 服务。但以现在的PlayBook来说,每次更改Nginx 配置文件后虽然可以通过它发布到生产服务器上,但整个PlayBook中的每个任务都要执行一次,这样无形中扩大了变更范围和变更风险
Tags 属性就可以解决这个问题
我们可以通过Play中的tags 属性,去解决目前PlayBook变更而导致的扩大变更范围和变更风险的问题。在改进的PlayBook中,针对文件发布TASK 任务 “update nginx main config” 和 “add virtualhost config” 新增了属性 tags ,属性值为updateconfig。另外我们新增"reload nginx server" TASK任务。当配置文件更新后,去reload Nginx 服务。那重新加载需要依赖于 Nginx 服务是已经启动状态。所以,还需要进一步通过判断 Nngix 的 pid 文件存在,才证明 Nginx 服务本身是启动中,启动中才可以 reload Nginx 服务。 判断一个文件是否存在使用 stat 模块
判断一个文件是否存在使用 stat
模块
- name: check nginx running
stat: path=/var/run/nginx.pid
register: nginxrunning
观察结果,会发现 nginxrunning.stat.exists
的值是 true
就表示启动状态,是 false
就是关闭状态。
- name: tags playbook example
hosts: webservers
gather_facts: no
vars:
createuser:
- tomcat
- www
- mysql
tasks:
- name: create user
user: name={{ item }} state=present
with_items: "{{ createuser }}"
- name: yum nginx webserver
yum: name=nginx state=present
- name: update nginx main config
copy: src=nginx.conf dest=/etc/nginx/
tags: updateconfig
- name: add virtualhost config
copy: src=www.qfedu.com.conf dest=/etc/nginx/conf.d/
tags: updateconfig
- name: check nginx syntax
shell: /usr/sbin/nginx -t
register: nginxsyntax
tags: updateconfig
- name: check nginx running
stat: path=/var/run/nginx.pid
register: nginxrunning
tags: updateconfig
- name: print nginx syntax
debug: var=nginxsyntax
- name: print nginx syntax
debug: var=nginxrunning
- name: reload nginx server
service: name=nginx state=reloaded
when: nginxsyntax.rc == 0 and nginxrunning.stat.exists == true
tags: updateconfig
- name: start nginx server
service: name=nginx state=started
when:
- nginxsyntax.rc == 0
- nginxrunning.stat.exists == false
tags: updateconfig
指定tags 去执行PlayBook
执行时一定要指定tags,这样再执行的过程中只会执行task 任务上打上tag 标记为 updateconfig 的任务
# ansible-playbook -i hosts site.yml -t updateconfig
结合Ansible的错误处理逻辑,上面的play漏洞百出
下面是我还改进的playbook
- name: tags playbook example
hosts: 10.11.67.32
gather_facts: no
vars:
createuser:
- tomcat
- www
- mysql
tasks:
- name: create user
user: name={{ item }} state=present
with_items: "{{ createuser }}"
- name: yum nginx webserver
yum: name=nginx state=present
- name: update nginx main config
copy: src=nginx.conf dest=/etc/nginx/
- name: add virtualhost config
copy: src=www.qfedu.com.conf dest=/etc/nginx/conf.d/
- name: check nginx syntax
shell: nginx -t
register: nginxsyntax
tags: kakaops
- name: check nginx running
shell: systemctl is-active nginx
register: nginxrunning
tags: kakaops
ignore_errors: yes
- name: print nginx syntax
debug: var=nginxsyntax
tags: kakaops
- name: print nginx syntax
debug: var=nginxrunning
tags: kakaops
- name: reload nginx config
systemd: name=nginx state=reloaded
when: nginxsyntax.rc == 0
tags: kakaops
- name: systemctl start nginx
systemd: name=nginx state=started
when:
- nginxsyntax.rc == 0
- nginxrunning.rc != 0
tags: kakaops
大体逻辑是这样的,检查配置文件的错误不能忽略,一旦配置文件出错,立即终止play,只有检查配置文件正确的,才继续往下执行,重新加载配置文件。检查nginx是否开启的错误输出可以用ignore_erroes=yes忽略,通过条件判断,如果没开启,就会开启nginx。判断nginx是否开启,我没有用stat模块,检查/var/run/nginx.pid是否存在,因为这样的检查方法存在一个bug。当nginx停止的时候,上面的play执行了一个nginx -t 的命令,产生/var/run/nginx.pid文件。这时候,nginx没有启动,但我们的启动逻辑会失效
五、Handlers属性
观察当前的 Playbook,细心的你会发现,当我的配置文件没有发生变化时,每次依然都会去触发TASK “reload nginx server”。如何能做到只有配置文件发生变化的时候才去触发TASK “reload nginx server”,这样的处理才是最完美的实现。此时可以使用 handlers 属性
handlers 英 [ˈhændləz] 美 [ˈhændlərz] n.处理程序,经办人
notify 英 [ˈnəʊtɪfaɪ] 美 [ˈnoʊtɪfaɪ] v.(正式)通报,通知
在改进的PlayBook中,针对文件发布TASK 任务 “update nginx main config” 和 “add virtualhost config” 增加了新属性 notify, 值为 “reload nginx server”。它的意思是说,针对这两个文件发布的TASK,设置一个通知机制,当Ansible 认为文件的内容发生了变化(文件MD5发生变化了),它就会发送一个通知信号,通知 handlers 中的某一个任务。具体发送到handlers中的哪个任务,由notify 的值"reload nginx server"决定。通知发出后handlers 会根据发送的通知,在handlers中相关的任务中寻找名称为"reload nginx server" 的任务。当发现存在这样名字的TASK,就会执行它。若没有找到,则什么也不做。若我们要实现这样的机制,千万要注意notify属性设置的值,一定要确保能和handlers中的TASK 名称对应上。修改任何一方的文件都会导致两端的文件不一致,都会触发任务的执行。
- name: test handlers
hosts: all
gather_facts: no
tasks:
- name: copy a file to remote server
copy:
src: a.txt
dest: /tmp/a.txt
notify:
- test ping
- ping a remote server
handlers:
- name: ping a remote server
ping:
- name: test ping
ping:
...
改进PlayBook
- name: handlers playbook example
hosts: webservers
gather_facts: no
vars:
createuser:
- tomcat
- www
- mysql
tasks:
- name: create user
user: name={{ item }} state=present
with_items: "{{ createuser }}"
- name: yum nginx webserver
yum: name=nginx state=present
- name: update nginx main config
copy: src=nginx.conf dest=/etc/nginx/
tags: updateconfig
notify: reload nginx server
- name: add virtualhost config
copy: src=www.qfedu.com.conf dest=/etc/nginx/conf.d/
tags: updateconfig
notify: reload nginx server
- name: check nginx syntax
shell: /usr/sbin/nginx -t
register: nginxsyntax
tags: updateconfig
- name: check nginx running
stat: path=/var/run/nginx.pid
register: nginxrunning
tags: updateconfig
- name: start nginx server
service: name=nginx state=started
when:
- nginxsyntax.rc == 0
- nginxrunning.stat.exists == false
handlers:
- name: reload nginx server
service: name=nginx state=reloaded
when:
- nginxsyntax.rc == 0
- nginxrunning.stat.exists == true
执行**
首次执行,若配置文件没有发生变化,可以发现根本就没有触发handlers 中TASK任务
# ansible-playbook -i hosts site.yml -t updateconfig
人为对Nginx 配置文件稍作修改,只要MD5校验值发生变化即可。此时再执行,发现触发了handlers 中的TASK任务
# ansible-playbook -i hosts site.yml -t updateconfig
六、Ansible中处理失败的任务
1、Ansible管理play中的任务错误的正常逻辑
Ansible评估任务的返回代码,从而确定任务是成功还是失败
通常而言,当任务失败时,Ansible将立即在该主机上中止play的其余部分并且跳过所有后续任务,但有些时候,可能希望即使在任务失败时也继续执行play
如果判断任务因为判断失败,被Ansible终止后续play的话,那么判断任务还有什么意义?
2、忽略任务失败 ignore_errors: yes
默认情况下,任务失败时play会中止。不过,可以通过忽略失败的任务来覆盖此行为。可以在任务中使用ignore_errors关键字来实现此目的
tasks:
- name: install httpd
yum:
name: packages //没有这个包
state: present
ignore_errors: yes //可选{yes、no}
//查看playbook
[root@localhost project]# cat playbook.yaml
---
- hosts: all
gather_facts: no
tasks:
- name: install httpd
yum:
name: packages //没有这个包
state: present
ignore_errors: yes //可选{yes、no}
- name: shoe some massage
debug:
msg: "hello word"
//执行play
[root@localhost project]# ansible-playbook playbook.yaml
PLAY [all] ****************************************************************************************************************************************************************
TASK [install httpd] ******************************************************************************************************************************************************
fatal: [client.example.com]: FAILED! => {"ansible_facts": {"discovered_interpreter_python": "/usr/libexec/platform-python"}, "changed": false, "failures": ["No package packages available."], "msg": "Failed to install some of the specified packages", "rc": 1, "results": []}
...ignoring //已经忽略这个任务出错
TASK [shoe some massage] **************************************************************************************************************************************************
ok: [client.example.com] => {
"msg": "hello word"
}
PLAY RECAP ****************************************************************************************************************************************************************
3、任务失败也强制执行处理程序(handlers)
在play中设置force_handlers: yes关键字,则即使play因为后续任务失败而中止也会调用被通知的处理程序(force:促使,推动)
//查看playbook
[root@localhost project]# cat playbook.yaml
-
hosts: all
force_handlers: yes //可选{yes、no}
tasks:-
name: install httpd
shell: ls //这条命令一定会执行成功,从而保证handlers处理程序一定会被触发
notify:- massage
-
name: install httpd
yum:
name: packages //没有这个包,肯定会出错
state: present
handlers:
- name: massage
debug:
msg: “hello word”
-
//查看playbook
[root@localhost project]# cat playbook.yaml
---
- hosts: all
force_handlers: yes //可选{yes、no}
tasks:
- name: install httpd
shell: ls //这条命令一定会执行成功,从而保证handlers处理程序一定会被触发
notify:
- massage
- name: install httpd
yum:
name: packages //没有这个包,肯定会出错
state: present
handlers:
- name: massage
debug:
msg: "hello word"
//执行play
[root@localhost project]# ansible-playbook playbook.yaml
PLAY [all] ****************************************************************************************************************************************************************
TASK [Gathering Facts] ****************************************************************************************************************************************************
ok: [client.example.com]
TASK [install httpd] ******************************************************************************************************************************************************
changed: [client.example.com]
TASK [install httpd] *****************************************************************************************************************************************************
fatal: [client.example.com]: FAILED! => {"changed": false, "failures": ["No package packages available."], "msg": "Failed to install some of the specified packages", "rc": 1, "results": []}
RUNNING HANDLER [massage] *************************************************************************************************************************************************
ok: [client.example.com] => {
"msg": "hello word"
}
PLAY RECAP ****************************************************************************************************************************************************************
client.example.com : ok=3 changed=1 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
处理程序会在任务报告changed结果时获得通知,而在任务报告ok或failed结果时不会获得通知
4、指定任务失败的条件
在任务中使用failed_when关键字来指定表示任务已失败的条件;通常与命令模块搭配使用,这些模块可能成功执行了某一命令,但命令的输出可能指示了失败
演示实例一:使用failed_when关键字
在playbook中执行脚本会以最后一个命令作为错误判断标准,中间错误命令不会影响整体的出错,同样也不会因为中间出错而报错
//查看使用的脚本
[root@localhost project]# cat files/test.sh
#!/bin/bash
cat /root //这句肯定会出错
echo "hello word"
//注意:在playbook中执行脚本会以最后一个命令作为错误判断标准,中间错误命令不会影响整体的出错,同样也不会因为中间出错而报错
//查看playbook,执行一次看是否成功
[root@localhost project]# cat playbook.yaml
---
- hosts: all
tasks:
- name: test
script:
files/test.sh
[root@localhost project]# ansible-playbook playbook.yaml
PLAY [all] ****************************************************************************************************************************************************************
TASK [Gathering Facts] ****************************************************************************************************************************************************
ok: [client.example.com]
TASK [test] ***************************************************************************************************************************************************************
changed: [client.example.com]
PLAY RECAP ****************************************************************************************************************************************************************
client.example.com : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
//这样无法判断是否都执行成功
//添加任务失败判断语句
[root@localhost project]# cat playbook.yaml
---
- hosts: all
tasks:
- name: test
script:
files/test.sh
register: result
failed_when: "'Is a directory' in result['stdout']"
[root@localhost project]# ansible-playbook playbook.yaml
PLAY [all] ****************************************************************************************************************************************************************
TASK [Gathering Facts] ****************************************************************************************************************************************************
ok: [client.example.com]
TASK [test] ***************************************************************************************************************************************************************
fatal: [client.example.com]: FAILED! => {"changed": true, "failed_when_result": true, "rc": 0, "stderr": "Shared connection to client.example.com closed.\r\n", "stderr_lines": ["Shared connection to client.example.com closed."], "stdout": "cat: /root: Is a directory\r\nhello word\r\n", "stdout_lines": ["cat: /root: Is a directory", "hello word"]}
PLAY RECAP ****************************************************************************************************************************************************************
client.example.com : ok=1 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
:fail模块也可用于强制任务失败(主要是将杂乱的提示信息通过自己设置提示方式,达到简单、明了的目的)
演示实例二
//查看playbook
[root@localhost project]# cat playbook.yaml
---
- hosts: all
tasks:
- name: test
script:
files/test.sh
register: result
- fail:
msg: "There have a failed"
when: "'Is a directory' in result['stdout']"
//执行play
[root@localhost project]# ansible-playbook playbook.yaml
PLAY [all] ****************************************************************************************************************************************************************
TASK [Gathering Facts] ****************************************************************************************************************************************************
ok: [client.example.com]
TASK [test] ***************************************************************************************************************************************************************
changed: [client.example.com]
TASK [fail] ***************************************************************************************************************************************************************
fatal: [client.example.com]: FAILED! => {"changed": false, "msg": "There have a failed"}
PLAY RECAP ****************************************************************************************************************************************************************
client.example.com : ok=2 changed=1 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
5、制定何时任务报告"changed"结果
当任务对托管主机进行了更改时,会报告 changed 状态并通知处理程序;如果任务不需要进行更改,则会报告ok并且不通知处理程序
使用changed_when关键字可用于控制任务在何时报告它已进行了更改
演示实例一
//查看playbook
[root@localhost project]# cat playbook.yaml
---
- hosts: all
tasks:
- name: test
shell: echo "hello word"
//执行后发现,每次都是changed
[root@localhost project]# ansible-playbook playbook.yaml
PLAY [all] ****************************************************************************************************************************************************************
TASK [Gathering Facts] ****************************************************************************************************************************************************
ok: [client.example.com]
TASK [test] ***************************************************************************************************************************************************************
changed: [client.example.com]
PLAY RECAP ****************************************************************************************************************************************************************
client.example.com : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
//添加changed_when关键字,以便报告OK
[root@localhost project]# cat playbook.yaml
---
- hosts: all
tasks:
- name: test
shell: echo "hello word"
changed_when: false //可选{true、false}
[root@localhost project]# ansible-playbook playbook.yaml
PLAY [all] ****************************************************************************************************************************************************************
TASK [Gathering Facts] ****************************************************************************************************************************************************
ok: [client.example.com]
TASK [test] ***************************************************************************************************************************************************************
ok: [client.example.com]
PLAY RECAP ****************************************************************************************************************************************************************
client.example.com : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
根据通过已注册变量收集的模块的输出来报告changed
//查看playbook
[root@localhost project]# cat playbook.yaml
---
- hosts: all
tasks:
- name: test
command: echo "hello word"
register: result
changed_when: "'hello word' in result['stdout']"
//执行play
[root@localhost project]# ansible-playbook playbook.yaml
PLAY [all] ****************************************************************************************************************************************************************
TASK [Gathering Facts] ****************************************************************************************************************************************************
ok: [client.example.com]
TASK [test] ***************************************************************************************************************************************************************
changed: [client.example.com]
PLAY RECAP ****************************************************************************************************************************************************************
client.example.com : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
//因为在result['stdout']中有hello word ,所以被认定为是true,所以就显示changed
6、Ansible块和错误处理
1️⃣:在playbook中,块是对任务进行逻辑分组的子句,可用于控制任务的执行方式
2️⃣:通过块,也可结合rescue和always语句来处理错误。如果块中的任何任务失败,则执行其rescue块中的任务来进行恢复
3️⃣:在block子句中的任务以及rescue子句中的任务(如果出现故障)运行之后,always子句中的任务运行
4️⃣:总结:
block:定义要运行的主要任务
rescue:定义要在block子句中定义的任务失败时运行的任务
always:定义始终都独立运行的任务,不论block和rescue子句中定义的任务是成功还是失败
5️⃣:演示:
演示实例一:当只有block和rescue,且block语句执行成功时,只执行block语句而不执行rescue语句(rescue:营救、救援)
//查看playbook
[root@localhost project]# cat playbook.yaml
---
- hosts: all
gather_facts: no
tasks:
- name: test
block:
- name: block
shell: echo "hello word"
rescue:
- name: rescue
shell: ls /root
//执行play
[root@localhost project]# ansible-playbook --syntax-check playbook.yaml
playbook: playbook.yaml
[root@localhost project]# an
anacron ansible-config ansible-console ansible-galaxy ansible-playbook ansible-test
ansible ansible-connection ansible-doc ansible-inventory ansible-pull ansible-vault
[root@localhost project]# ansible-playbook playbook.yaml
PLAY [all] ****************************************************************************************************************************************************************
TASK [block] **************************************************************************************************************************************************************
changed: [client.example.com]
PLAY RECAP ****************************************************************************************************************************************************************
client.example.com : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
//可以发现,只执行了block语句,并没有执行rescue语句
演示实例二:当只有block和rescue,且block语句执行失败时,不执行block语句而执行rescue语句
//查看playbook
[root@localhost project]# cat playbook.yaml
---
- hosts: all
gather_facts: no
tasks:
- name: test
block:
- name: block
command: cat / //这句肯定会失败
rescue:
- name: rescue
shell: ls /root
//执行play
[root@localhost project]# ansible-playbook playbook.yaml
PLAY [all] ****************************************************************************************************************************************************************
TASK [block] **************************************************************************************************************************************************************
fatal: [client.example.com]: FAILED! => {"ansible_facts": {"discovered_interpreter_python": "/usr/libexec/platform-python"}, "changed": true, "cmd": ["cat", "/"], "delta": "0:00:00.005350", "end": "2020-09-08 10:59:18.381699", "msg": "non-zero return code", "rc": 1, "start": "2020-09-08 10:59:18.376349", "stderr": "cat: /: Is a directory", "stderr_lines": ["cat: /: Is a directory"], "stdout": "", "stdout_lines": []}
TASK [rescue] *************************************************************************************************************************************************************
changed: [client.example.com]
PLAY RECAP ****************************************************************************************************************************************************************
client.example.com : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=1 ignored=0
演示实例三:当block语句、rescue语句和always语句都有时,无论block语句是否失败,always语句总是执行
//查看playbook
[root@localhost project]# cat playbook.yaml
---
- hosts: all
gather_facts: no
tasks:
- name: test
block:
- name: block
command: cat /
rescue:
- name: rescue
shell: ls /root
always:
- name: always
debug:
msg: "This is my test"
//执行play
[root@localhost project]# ansible-playbook playbook.yaml
PLAY [all] ****************************************************************************************************************************************************************
TASK [block] **************************************************************************************************************************************************************
fatal: [client.example.com]: FAILED! => {"ansible_facts": {"discovered_interpreter_python": "/usr/libexec/platform-python"}, "changed": true, "cmd": ["cat", "/"], "delta": "0:00:00.008993", "end": "2020-09-08 11:05:47.816489", "msg": "non-zero return code", "rc": 1, "start": "2020-09-08 11:05:47.807496", "stderr": "cat: /: Is a directory", "stderr_lines": ["cat: /: Is a directory"], "stdout": "", "stdout_lines": []}
TASK [rescue] *************************************************************************************************************************************************************
changed: [client.example.com]
TASK [always] *************************************************************************************************************************************************************
ok: [client.example.com] => {
"msg": "This is my test"
}
PLAY RECAP ****************************************************************************************************************************************************************
client.example.com : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=1 ignored=0
6️⃣:block中的when条件也会应用到其rescue和always子句(若存在)
演示实例一:
//查看playbook
[root@localhost project]# cat playbook.yaml
---
- hosts: all
gather_facts: no
tasks:
- name: test
block:
- name: block
command: echo "hello word" //该语句没有错误
when: ansible_facts['distribution'] == "CentOS" //条件判断出错会导致block语句不会执行
rescue:
- name: rescue
shell: ls /root
always:
- name: always
debug:
msg: "This is my test"
//执行play
[root@localhost project]# ansible-playbook playbook.yaml
PLAY [all] ****************************************************************************************************************************************************************
TASK [block] **************************************************************************************************************************************************************
fatal: [client.example.com]: FAILED! => {"msg": "The conditional check 'ansible_facts['distribution'] == \"CentOS\"' failed. The error was: error while evaluating conditional (ansible_facts['distribution'] == \"CentOS\"): 'dict object' has no attribute 'distribution'\n\nThe error appears to be in '/root/project/playbook.yaml': line 7, column 11, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n block:\n - name: block\n ^ here\n"}
TASK [rescue] *************************************************************************************************************************************************************
changed: [client.example.com]
TASK [always] *************************************************************************************************************************************************************
ok: [client.example.com] => {
"msg": "This is my test"
}
PLAY RECAP ****************************************************************************************************************************************************************