ansible if流程控制
大部分的Ansible任务,需要对用户的输入内容或任务的运行结果进行判断,这中间体现了流程控制的作用
像ansible的模板文件,以.j2结尾的都是Jinja2文件类型,可以使用Jinj2语法
Ansible在配置 模板文件 和进行 条件判断 时都会用到Jinja2语法
Jinja2语法官网
Jinja2是一套模板引擎,用于python与HTML页面关联,提供变量,控制结构,过滤器
变量
用 {{ }}表示变量,变量定义与python相同
<!-- 输出字符串 -->
<p>{{ greeting }}</p>
<!-- 输出列表 -->
<ul>
{% for item in items %}
<li>{{ item }}</li>
{% endfor %}
</ul>
<!-- 输出字典 -->
<p>Name: {{ user.name }}, Age: {{ user.age }}</p>
控制结构
判断语句
<p>
{% if condition %}
This is true.
{% elif another_condition %}
This is another condition.
{% else %}
This is false.
{% endif %}
</p>
Jinja2支持for循环,用于迭代列表,元组,字典
循环语句
<ul>
{% for item in items %}
<li>{{ item }}</li>
{% endfor %}
</ul>
<!-- 迭代字典 -->
<dl>
{% for key, value in my_dict.items() %}
<dt>{{ key }}</dt>
<dd>{{ value }}</dd>
{% endfor %}
</dl>
过滤器
过滤器通过管道符 |,实现对变量的格式化或转换
常用过滤器
- safe:渲染时值不转义。
- capitalize:把值的首字母转换成大写,其他字母转换为小写。
- lower:把值转换成小写形式。
- upper:把值转换成大写形式。
- title:把值中每个单词的首字母都转换成大写。
- trim:把值的首尾空格去掉。
- striptags:渲染之前把值中所有的HTML标签都删掉。
- join:拼接多个值为字符串。
- replace:替换字符串的值。
- round:默认对数字进行四舍五入,也可以用参数进行控制。
- int:把值转换成整型。
<!-- 应用upper过滤器 -->
<p>{{ text|upper }}</p>
<!-- 链式调用过滤器 -->
<p>{{ "hello world" | replace('world','daxin') | upper }}</p>
注释
{# 这是一个注释,不会在最终页面显示 #}
Jinja2 api示例
此示例不是Jinja2模板,而是python代码
通过Template类创建实例,得到模板对象,调用对象的render方法填充变量名,实现模板替换from jinja2 import Template
template = Template('hello {{ name }}')
template.render(name='world')
# 输出结果
'hello world'
下列表达式的运算结果都为'true':
1 in [1, 2, 3]
'see' in 'Can you see me?'
foo='foo'
bar='bar'
foo != bar
(1 < 2) and ('a' not in 'best')
# 下列表达式的运算结果都为'false':
4 in [1, 2, 3]
foo == bar
a=2
(foo != foo) or (a in [1, 2, 3])
test 语句
python-test.py
from jinja2 import Template
# 定义模板字符串
template_str = """
{# 这里用于检查foo 是否定义,这也可以写在*.j2文件中,用于模板文件 #}
{% if foo is defined %}
foo is defined and its value is: {{ foo }}
{% else %}
foo is not defined
{% endif %}
{% if bar is undefined %}
bar is defined and its value is: {{ bar }}
{% else %}
bar is not defined
{% endif %}
{% if foo is equalto "Hello, Jinja2!" %}
foo is defined and its value is: {{ foo }}
{% else %}
foo is not defined
{% endif %}
{% if 10 is even %}
10 is : {{ even }}
{% else %}
10 is not even
{% endif %}
tmp_list=[]
{% if tmp_list is iterable %}
tmp_list is : iterable
{% else %}
tmp_list is not iterable
{% endif %}
"""
# 创建一个 Jinja2 模板对象
template = Template(template_str)
# 渲染模板,提供上下文数据
output = template.render(foo="Hello, Jinja2!") # 这里定义了变量 foo
# 输出结果
print(output)
-
defined相反(判断是否定义)
-
undefined(判断是否未定义)
-
equalto(与==等效)
-
even(判断对象是否是偶数)
-
iterable(判断对象是否可迭代)
ansible when流程控制
when 语句用于在任务中添加条件判断.只有当 when 条件为 True 时,任务才会被执行
简单示例
when 语句通常用于控制任务的执行逻辑,帮助你根据不同的变量值或条件动态地执行特定的任务
---
- hosts: ubuntu
tasks:
- name: Install Apache on Debian-based systems
apt:
name: apache2
state: present
when: ansible_facts['os_family'] == "Debian"
只有在目标主机的操作系统家族 os_family 为 Debian 时,任务才会执行
多条件示例
你可以使用 and 和 or 逻辑运算符组合多个条件
---
- hosts: ubuntu
tasks:
- name: Install Apache if on Debian and not on Ubuntu
debug:
msg: "os_family is Debian and distribution is not ubuntu"
when: (ansible_facts['os_family'] == "Debian") and (ansible_facts['distribution'] != "Ubuntu")
操作系统家族为 Debian 且发行版不是 Ubuntu 时执行
以下语句与上面效果相同
- name: 条件1 and 条件2
when: (ansible_facts['os_family'] == "Debian") and (ansible_facts['distribution'] != "Ubuntu")
#这种条件更加清晰易读
- name: 条件1 and 条件2
when:
- ansible_facts['os_family'] == "Debian"
- ansible_facts['distribution'] != "Ubuntu"
#使用not关键字
- name: 条件1 and 条件2
when: (ansible_facts['os_family'] == "Debian") and not (ansible_facts['distribution'] == "Ubuntu")
#使用了Jinja2的lower过滤器来确保分发版的名称是小写的,这样可以提高条件的健壮性,防止因为大小写不匹配而导致条件判断失败
- name: 条件1 and 条件2
when: ansible_facts['os_family'] == "Debian" and ansible_facts['distribution'] | lower != "ubuntu"
changed_when 流程控制
当使用command或shell模块时,任务的返回状态永远是changed,这是因为ansible无法事先判断命令执行是否会对系统进行更改,changed状态表明导致了系统状态更改
ok表示执行成功,但系统没有任何改变changed_when 只会返回ok和changed状态
使changed_when语句,需要对command,shell模块执行的命令返回有一定了解,有些命令返回到stdout,有些返回到stderr,这种自由的返回状态,需要根据实际情况编写对应检查代码
示例如下
---
- hosts: ubuntu
tasks:
- name: run set_timezone command
shell: 'timedatectl set-timezone Asia/Shanghai ; timedatectl status | grep "Time zone"'
register: set_timezone_command
changed_when: "'Shanghai' in set_timezone_command.stdout"
- name: print set_timezone command
debug:
msg:
- "{{ set_timezone_command }}"
- "{{ set_timezone_command.stdout }}"
- "{{ set_timezone_command.stdout_lines }}"
- "{{ set_timezone_command.stderr }}"
ignore_errors 流程控制
ignore_errors 不能直接基于 register 中的变量来动态决定是否忽略错误。ignore_errors 是在任务执行之前解析的,而 register 中的变量是在任务执行之后才会生成。因此,您不能在同一个任务中使用 register 变量来动态决定 ignore_errors 的行为
您可以使用 when 条件来基于 register 的结果来决定是否执行后续的任务,或者通过 block 结构来控制错误处理的逻辑
---
- hosts: ubuntu
vars:
ignore: 'yes'
tasks:
- name: Run a command and ignore errors
command: /bin/false
ignore_errors: true
- name: ignore by vars
command: /bin/false
ignore_errors: "{{ ignore == 'yes' }}"
浙公网安备 33010602011771号