当想要在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等。
各个元素的解释如下所示。

key名valueの説明failed失敗したかどうかのboolean値itemwith_itemsで指定した値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
广告
将在 10 秒后关闭
bannerAds