当想要在Ansible中进行循环条件分支时
为了达到某种预定的结果或目标。
我认为有时候我们希望检查已安装的软件包,并根据有无进行不同的执行处理。这次我将以asdf插件为例进行验证,但也适用于其他软件包管理器。当然,如果有相应的模块可用,最好使用该模块。但是,尝试创建模块时,会经常遇到这个问题。
目标环境
> ansible --version
ansible 2.7.1
config file = None
configured module search path = ['/Users/loveansibleman/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /usr/local/Cellar/ansible/2.7.1/libexec/lib/python3.7/site-packages/ansible
executable location = /usr/local/bin/ansible
python version = 3.7.1 (default, Nov 11 2018, 22:35:12) [Clang 10.0.0 (clang-1000.11.45.5)]
途径
对变量进行赋值
首先,关于变量赋值部分,基本上只需要在with_items相同的部分进行记录即可。
这次我想要确认asdf插件,并进行条件分支,所以我用grep命令在asdf plugin-list的结果中指定需要的语言,并在with_items下面进行了grep操作。
在asdf的情况下,需要加载.bash_profile并进行执行,所以我在开头加上了/bin/bash -lc。
当安装rbenv等工具时,也会遇到类似的情况,根据需要进行操作。
- name: Get installed plugin list
shell: /bin/bash -lc "asdf plugin-list | grep {{ item }}"
register: result
with_items:
- python
- ruby
- golang
- java
ignore_errors: yes
changed_when: no
如果grep没有匹配到任何结果,则会报错,所以我们将ignore_errors: yes设为true,并且如果命令成功执行,则将changed_when: no设置为false。
结果如下。
TASK [Get installed plugin list] ***************************************************************
ok: [127.0.0.1] => (item=python)
ok: [127.0.0.1] => (item=ruby)
ok: [127.0.0.1] => (item=golang)
failed: [127.0.0.1] (item=java) => {"changed": false, "cmd": "/bin/bash -lc \"asdf plugin-list | grep java\"", "delta": "0:00:00.574073", "end": "2019-01-26 10:42:38.981170", "item": "java", "msg": "non-zero return code", "rc": 1, "start": "2019-01-26 10:42:38.407097", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []}
...ignoring
由于存在Python、Ruby和Golang,因此使用grep不会出错,而且由于加入了changed_when: no,它也不会产生任何更改,结果是ok。
由于Java不存在,因此会出错,但由于加入了ignore_errors: yes,它被忽略。
变量的内容
想要查看结果内容,因此执行以下Playbook。
- name: View result
debug:
var: result
然后这里是结果。
TASK [View result] *****************************************************************************
ok: [127.0.0.1] => {
"result": {
"changed": false,
"failed": true,
"msg": "All items completed",
"results": [
{
"_ansible_ignore_errors": true,
"_ansible_item_label": "python",
"_ansible_item_result": true,
"_ansible_no_log": false,
"_ansible_parsed": true,
"changed": false,
"cmd": "/bin/bash -lc \"asdf plugin-list | grep python\"",
"delta": "0:00:00.694997",
"end": "2019-01-26 10:42:36.372877",
"failed": false,
"invocation": {
"module_args": {
"_raw_params": "/bin/bash -lc \"asdf plugin-list | grep python\"",
"_uses_shell": true,
"argv": null,
"chdir": null,
"creates": null,
"executable": null,
"removes": null,
"stdin": null,
"warn": true
}
},
"item": "python",
"rc": 0,
"start": "2019-01-26 10:42:35.677880",
"stderr": "",
"stderr_lines": [],
"stdout": "python",
"stdout_lines": [
"python"
]
},
{
"_ansible_ignore_errors": true,
"_ansible_item_label": "ruby",
"_ansible_item_result": true,
"_ansible_no_log": false,
"_ansible_parsed": true,
"changed": false,
"cmd": "/bin/bash -lc \"asdf plugin-list | grep ruby\"",
"delta": "0:00:00.584345",
"end": "2019-01-26 10:42:37.258385",
"failed": false,
"invocation": {
"module_args": {
"_raw_params": "/bin/bash -lc \"asdf plugin-list | grep ruby\"",
"_uses_shell": true,
"argv": null,
"chdir": null,
"creates": null,
"executable": null,
"removes": null,
"stdin": null,
"warn": true
}
},
"item": "ruby",
"rc": 0,
"start": "2019-01-26 10:42:36.674040",
"stderr": "",
"stderr_lines": [],
"stdout": "ruby",
"stdout_lines": [
"ruby"
]
},
{
"_ansible_ignore_errors": true,
"_ansible_item_label": "golang",
"_ansible_item_result": true,
"_ansible_no_log": false,
"_ansible_parsed": true,
"changed": false,
"cmd": "/bin/bash -lc \"asdf plugin-list | grep golang\"",
"delta": "0:00:00.579275",
"end": "2019-01-26 10:42:38.121094",
"failed": false,
"invocation": {
"module_args": {
"_raw_params": "/bin/bash -lc \"asdf plugin-list | grep golang\"",
"_uses_shell": true,
"argv": null,
"chdir": null,
"creates": null,
"executable": null,
"removes": null,
"stdin": null,
"warn": true
}
},
"item": "golang",
"rc": 0,
"start": "2019-01-26 10:42:37.541819",
"stderr": "",
"stderr_lines": [],
"stdout": "golang",
"stdout_lines": [
"golang"
]
},
{
"_ansible_item_label": "java",
"_ansible_item_result": true,
"_ansible_no_log": false,
"_ansible_parsed": true,
"changed": false,
"cmd": "/bin/bash -lc \"asdf plugin-list | grep java\"",
"delta": "0:00:00.574073",
"end": "2019-01-26 10:42:38.981170",
"failed": true,
"invocation": {
"module_args": {
"_raw_params": "/bin/bash -lc \"asdf plugin-list | grep java\"",
"_uses_shell": true,
"argv": null,
"chdir": null,
"creates": null,
"executable": null,
"removes": null,
"stdin": null,
"warn": true
}
},
"item": "java",
"msg": "non-zero return code",
"rc": 1,
"start": "2019-01-26 10:42:38.407097",
"stderr": "",
"stderr_lines": [],
"stdout": "",
"stdout_lines": []
}
]
}
}
当查看这个时,可以看到result.results中包含了with_items每个项目的结果作为一个数组的形式。
该数组的元素包括了failed、item、cmd等。
各个元素的解释如下所示。
换句话说,我们发现可以通过对result.results进行循环并通过failed进行条件分支。
此外,如果需要,可以在循环内使用item和cmd。
条件分支
根据上述的结果,将result.results放入with_items中,在”when”后面添加”not item.failed”,可以进行存在情况的条件判断。将”item.failed”放入,则可以进行不存在情况的条件判断。下面是相应的设置。
- name: If listed items
debug:
msg: "{{ item.item }} is found"
with_items: '{{ result.results }}'
when: not item.failed
- name: If not listed items
debug:
msg: "{{ item.item }} is not found"
with_items: '{{ result.results }}'
when: item.failed
以下是结果。
TASK [If listed items] *************************************************************************
ok: [127.0.0.1] => (item={'changed': False, 'end': '2019-01-26 10:42:36.372877', 'stdout': 'python', 'cmd': '/bin/bash -lc "asdf plugin-list | grep python"', 'rc': 0, 'start': '2019-01-26 10:42:35.677880', 'stderr': '', 'delta': '0:00:00.694997', 'invocation': {'module_args': {'creates': None, 'executable': None, '_uses_shell': True, '_raw_params': '/bin/bash -lc "asdf plugin-list | grep python"', 'removes': None, 'argv': None, 'warn': True, 'chdir': None, 'stdin': None}}, '_ansible_parsed': True, 'stdout_lines': ['python'], 'stderr_lines': [], '_ansible_no_log': False, 'failed': False, 'item': 'python', '_ansible_item_result': True, '_ansible_ignore_errors': True, '_ansible_item_label': 'python'}) => {
"msg": "python is found"
}
ok: [127.0.0.1] => (item={'changed': False, 'end': '2019-01-26 10:42:37.258385', 'stdout': 'ruby', 'cmd': '/bin/bash -lc "asdf plugin-list | grep ruby"', 'rc': 0, 'start': '2019-01-26 10:42:36.674040', 'stderr': '', 'delta': '0:00:00.584345', 'invocation': {'module_args': {'creates': None, 'executable': None, '_uses_shell': True, '_raw_params': '/bin/bash -lc "asdf plugin-list | grep ruby"', 'removes': None, 'argv': None, 'warn': True, 'chdir': None, 'stdin': None}}, '_ansible_parsed': True, 'stdout_lines': ['ruby'], 'stderr_lines': [], '_ansible_no_log': False, 'failed': False, 'item': 'ruby', '_ansible_item_result': True, '_ansible_ignore_errors': True, '_ansible_item_label': 'ruby'}) => {
"msg": "ruby is found"
}
ok: [127.0.0.1] => (item={'changed': False, 'end': '2019-01-26 10:42:38.121094', 'stdout': 'golang', 'cmd': '/bin/bash -lc "asdf plugin-list | grep golang"', 'rc': 0, 'start': '2019-01-26 10:42:37.541819', 'stderr': '', 'delta': '0:00:00.579275', 'invocation': {'module_args': {'creates': None, 'executable': None, '_uses_shell': True, '_raw_params': '/bin/bash -lc "asdf plugin-list | grep golang"', 'removes': None, 'argv': None, 'warn': True, 'chdir': None, 'stdin': None}}, '_ansible_parsed': True, 'stdout_lines': ['golang'], 'stderr_lines': [], '_ansible_no_log': False, 'failed': False, 'item': 'golang', '_ansible_item_result': True, '_ansible_ignore_errors': True, '_ansible_item_label': 'golang'}) => {
"msg": "golang is found"
}
skipping: [127.0.0.1] => (item={'changed': False, 'end': '2019-01-26 10:42:38.981170', 'stdout': '', 'cmd': '/bin/bash -lc "asdf plugin-list | grep java"', 'failed': True, 'delta': '0:00:00.574073', 'stderr': '', 'rc': 1, 'invocation': {'module_args': {'creates': None, 'executable': None, '_uses_shell': True, '_raw_params': '/bin/bash -lc "asdf plugin-list | grep java"', 'removes': None, 'argv': None, 'warn': True, 'chdir': None, 'stdin': None}}, 'start': '2019-01-26 10:42:38.407097', 'msg': 'non-zero return code', '_ansible_parsed': True, 'stdout_lines': [], 'stderr_lines': [], '_ansible_no_log': False, 'item': 'java', '_ansible_item_result': True, '_ansible_item_label': 'java'})
TASK [If not listed items] ********************************************************************
skipping: [127.0.0.1] => (item={'changed': False, 'end': '2019-01-26 10:42:36.372877', 'stdout': 'python', 'cmd': '/bin/bash -lc "asdf plugin-list | grep python"', 'rc': 0, 'start': '2019-01-26 10:42:35.677880', 'stderr': '', 'delta': '0:00:00.694997', 'invocation': {'module_args': {'creates': None, 'executable': None, '_uses_shell': True, '_raw_params': '/bin/bash -lc "asdf plugin-list | grep python"', 'removes': None, 'argv': None, 'warn': True, 'chdir': None, 'stdin': None}}, '_ansible_parsed': True, 'stdout_lines': ['python'], 'stderr_lines': [], '_ansible_no_log': False, 'failed': False, 'item': 'python', '_ansible_item_result': True, '_ansible_ignore_errors': True, '_ansible_item_label': 'python'})
skipping: [127.0.0.1] => (item={'changed': False, 'end': '2019-01-26 10:42:37.258385', 'stdout': 'ruby', 'cmd': '/bin/bash -lc "asdf plugin-list | grep ruby"', 'rc': 0, 'start': '2019-01-26 10:42:36.674040', 'stderr': '', 'delta': '0:00:00.584345', 'invocation': {'module_args': {'creates': None, 'executable': None, '_uses_shell': True, '_raw_params': '/bin/bash -lc "asdf plugin-list | grep ruby"', 'removes': None, 'argv': None, 'warn': True, 'chdir': None, 'stdin': None}}, '_ansible_parsed': True, 'stdout_lines': ['ruby'], 'stderr_lines': [], '_ansible_no_log': False, 'failed': False, 'item': 'ruby', '_ansible_item_result': True, '_ansible_ignore_errors': True, '_ansible_item_label': 'ruby'})
skipping: [127.0.0.1] => (item={'changed': False, 'end': '2019-01-26 10:42:38.121094', 'stdout': 'golang', 'cmd': '/bin/bash -lc "asdf plugin-list | grep golang"', 'rc': 0, 'start': '2019-01-26 10:42:37.541819', 'stderr': '', 'delta': '0:00:00.579275', 'invocation': {'module_args': {'creates': None, 'executable': None, '_uses_shell': True, '_raw_params': '/bin/bash -lc "asdf plugin-list | grep golang"', 'removes': None, 'argv': None, 'warn': True, 'chdir': None, 'stdin': None}}, '_ansible_parsed': True, 'stdout_lines': ['golang'], 'stderr_lines': [], '_ansible_no_log': False, 'failed': False, 'item': 'golang', '_ansible_item_result': True, '_ansible_ignore_errors': True, '_ansible_item_label': 'golang'})
ok: [127.0.0.1] => (item={'changed': False, 'end': '2019-01-26 10:42:38.981170', 'stdout': '', 'cmd': '/bin/bash -lc "asdf plugin-list | grep java"', 'failed': True, 'delta': '0:00:00.574073', 'stderr': '', 'rc': 1, 'invocation': {'module_args': {'creates': None, 'executable': None, '_uses_shell': True, '_raw_params': '/bin/bash -lc "asdf plugin-list | grep java"', 'removes': None, 'argv': None, 'warn': True, 'chdir': None, 'stdin': None}}, 'start': '2019-01-26 10:42:38.407097', 'msg': 'non-zero return code', '_ansible_parsed': True, 'stdout_lines': [], 'stderr_lines': [], '_ansible_no_log': False, 'item': 'java', '_ansible_item_result': True, '_ansible_item_label': 'java'}) => {
"msg": "java is not found"
}
在Python、Ruby和Golang中,可以看出第一个playbook被执行,而在Java中,第一个被跳过,第二个被执行。太好了!
概括
请使用下方的playbook进行本次操作。
- name: Get installed plugin list
shell: /bin/bash -lc "asdf plugin-list | grep {{ item }}"
register: result
with_items:
- python
- ruby
- golang
- java
ignore_errors: yes
changed_when: no
- name: View result
debug:
var: result
- name: If listed items
debug:
msg: "{{ item.item }} is found"
with_items: '{{ result.results }}'
when: not item.failed
- name: If not listed items
debug:
msg: "{{ item.item }} is not found"
with_items: '{{ result.results }}'
when: item.failed
请在以下提供的选项中选择一个:
参考文献
-
- Ansible – using with_items and when conditional to – Stack Overflow
- Ansible shell module returns error when grep results are empty – Stack Overflow