Ansible register result of multiple commands

2020-05-19 06:09发布

I was given a task to verify some routing entries for all Linux server and here is how I did it using an Ansible playbook

---
  - hosts: Linux
    serial: 1

    tasks:
      - name: Check first
        command: /sbin/ip route list xxx.xxx.xxx.xxx/24
        register: result
        changed_when: false

      - debug: msg="{{result.stdout}}"

      - name: Check second
        command: /sbin/ip route list xxx.xxx.xxx.xxx/24
        register: result
        changed_when: false

      - debug: msg="{{result.stdout}}"

You can see I have to repeat same task for each routing entry and I believe I should be able to avoid this. I tried use with_items loop but got following error message

One or more undefined variables: 'dict object' has no attribute 'stdout'

is there a way to register variable for each command and loop over them one by one ?

标签: ansible
4条回答
放我归山
2楼-- · 2020-05-19 06:17

Starting in Ansible 1.6.1, the results registered with multiple items are stored in result.results as an array. So you can use result.results[0].stdout and so on.

Testing playbook:

---
- hosts: localhost
  gather_facts: no
  tasks:
    - command: "echo {{item}}"
      register: result
      with_items: [1, 2]
    - debug:
        var: result

Result:

$ ansible-playbook -i localhost, test.yml

PLAY [localhost] ************************************************************** 

TASK: [command echo {{item}}] ************************************************* 
changed: [localhost] => (item=1)
changed: [localhost] => (item=2)

TASK: [debug ] **************************************************************** 
ok: [localhost] => {
    "var": {
        "result": {
            "changed": true, 
            "msg": "All items completed", 
            "results": [
                {
                    "changed": true, 
                    "cmd": [
                        "echo", 
                        "1"
                    ], 
                    "delta": "0:00:00.002502", 
                    "end": "2015-08-07 16:44:08.901313", 
                    "invocation": {
                        "module_args": "echo 1", 
                        "module_name": "command"
                    }, 
                    "item": 1, 
                    "rc": 0, 
                    "start": "2015-08-07 16:44:08.898811", 
                    "stderr": "", 
                    "stdout": "1", 
                    "stdout_lines": [
                        "1"
                    ], 
                    "warnings": []
                }, 
                {
                    "changed": true, 
                    "cmd": [
                        "echo", 
                        "2"
                    ], 
                    "delta": "0:00:00.002516", 
                    "end": "2015-08-07 16:44:09.038458", 
                    "invocation": {
                        "module_args": "echo 2", 
                        "module_name": "command"
                    }, 
                    "item": 2, 
                    "rc": 0, 
                    "start": "2015-08-07 16:44:09.035942", 
                    "stderr": "", 
                    "stdout": "2", 
                    "stdout_lines": [
                        "2"
                    ], 
                    "warnings": []
                }
            ]
        }
    }
}

PLAY RECAP ******************************************************************** 
localhost                  : ok=2    changed=1    unreachable=0    failed=0   
查看更多
beautiful°
3楼-- · 2020-05-19 06:26

Posting because I can't comment yet

Relating to gameweld's answer, since Ansible 2.5 there's another way to accessing the iteration index.

From the docs:

Tracking progress through a loop with index_var

New in version 2.5.

To keep track of where you are in a loop, use the index_var directive with loop_control. This directive specifies a variable name to contain the current loop index:

- name: count our fruit
  debug:
    msg: "{{ item }} with index {{ my_idx }}"
  loop:
    - apple
    - banana
    - pear
  loop_control:
    index_var: my_idx

This also allows you to gather results from an array and act later to the same array, taking into account the previous results

- name: Ensure directories exist
  file:
    path: "{{ item }}"
    state: directory
  loop:
    - "mouse"
    - "lizard"
  register: reg

- name: Do something only if directory is new
  debug:
    msg: "New dir created with name '{{ item }}'"
  loop:
   - "mouse"
   - "lizard"
  loop_control:
    index_var: index
  when: reg.results[index].changed

Please note that the "mouse lizard" array should be exactly the same

查看更多
一夜七次
4楼-- · 2020-05-19 06:28

If what you need is to register the output of two commands separately, use different variable names.

---
- hosts: Linux
  serial: 1
  tasks:
  - name: Check first
    command: /sbin/ip route list xxx.xxx.xxx.xxx/24
    register: result0
    changed_when: false

  - debug: msg="{{result0.stdout}}"

  - name: Check second
    command: /sbin/ip route list xxx.xxx.xxx.xxx/24
    register: result1
    changed_when: false

  - debug: msg="{{result1.stdout}}"
查看更多
Rolldiameter
5楼-- · 2020-05-19 06:40

A slightly different situation, which took a while to figure out. If you want to use the results of multiple items, but for changed_when, then the register variable will not have a var.results! Instead, changed_when, is evaluated for each item, and you can just directly use the register var.

Simple example, which will result in changed: false:

- action: command echo {{item}}
  register: out
  changed_when: "'z' in out.stdout"
  with_items:
    - hello
    - foo
    - bye

Another example:

- name: Create fulltext index for faster text searches.
  mysql_db: name={{SO_database}} state=import target=/tmp/fulltext-{{item.tableName}}-{{item.columnName}}.sql
  with_items: 
    - {tableName: Posts,  columnName: Title}
    - {tableName: Posts,  columnName: Body}
    - {tableName: Posts,  columnName: Tags}
    - {tableName: Comments, columnName: Text}
  register: createfulltextcmd
  changed_when: createindexcmd.msg.find('already exists') == -1

Finally, when you do want to loop through results in other contexts, it does seem a bit tricky to programmatically access the index as that is not exposed. I did find this one example that might be promising:

- name: add hosts to known_hosts
shell: 'ssh-keyscan -H {{item.host}}>> /home/testuser/known_hosts'
with_items:
  - { index: 0, host: testhost1.test.dom }
  - { index: 1, host: testhost2.test.dom }
  - { index: 2, host: 192.168.202.100 }
when: ssh_known_hosts.results[{{item.index}}].rc == 1
查看更多
登录 后发表回答