How to skip all other plays in ansible playbook if some condition is not met?

馋奶兔 提交于 2020-01-06 02:25:27

问题


I have multiple plays in below playbook. I want to ignore all other plays if some condition is not met.

So for below example - If I cannot find any new file in Play1 then I don't want to execute Play2 and Play3 at all (it should skip it). How can I do that?

I have end_play in Play1 but it only skips Play1 and it still executes Play2 and Play3

---
- name: Play 1
  hosts: 127.0.0.1
  tasks:
      - name: find the latest file
        find: paths=/var/lib/jenkins/jobs/process/workspace/files
              file_type=file
              age=-1m
              age_stamp=mtime
        register: files

      - meta: end_play
        when: files.files|count == 0

      - name: Copy file, if found
        copy:
          src: "some stuff here"
          dest: "some other stuff here"
        when: files.files|count > 0

- name: Play 2
  hosts: all
  serial: 5
  tasks:
      - name: copy latest file
        copy: src=data_init/goldy.init.qa dest=/data01/admin/files/goldy.init.qa owner=golden group=golden

      - name: copy latest file
        copy: src=data_init/goldy.init.qa dest=/data02/admin/files/goldy.init.qa owner=golden group=golden

- name: Play 3
  hosts: 127.0.0.1
  tasks:
      - name: execute command
        shell: ./data_init --init_file ./goldy.init.qa
        args:
          chdir: /var/lib/jenkins/jobs/process/workspace/data_init/

Update:

So block module solution doesn't work looks like because you cannot use nested play in block like that..


回答1:


There are two ways to approach your issue:

  1. Although it is understandable that you tried defining a hosts: localhost to run some tasks on the controller, you don't really need this. Using delegate_to: localhost and run_once: true on the steps you want to run on your controller is a far better approach.
  2. If you really want to go this way (using hosts: localhost), for some reason, then you'll need to save the return of your find, with set_fact in order to reuse it in other hosts with the help of the global hostvars

Using delegation

Any task in the play can be delegated to another host. This is quite simple to do and would just use one extra delegate_to options on your task:

- name: Delegate a find to localhost
  find: 
    path: /test
    file_type: any
  register: localhost_find
  delegate_to: localhost
  run_once: true

When doing so, I would advice you to also use run_once: true, because, if you delegate the task to another host, there is no need to, let's say run it ten times if you have ten hosts in your host group.

Here is a little example about that

---
- hosts: hosts
  become: true
  gather_facts: false

  tasks:
    - name: Delegate directory creation to localhost
      file: 
        path: /test
        state: directory
      delegate_to: localhost
      run_once: true

    - name: Create directory on hosts
      file:
        path: /test                 
        state: directory

    - name: Delegate file creation to localhost
      file:
        path: /test/localhost.txt                 
        state: touch
      delegate_to: localhost
      run_once: true

    - name: Create file on hosts
      file:
        path: /test/host.txt
        state: touch

    - name: Delegate a find to localhost
      find: 
        path: /test
        file_type: any
      register: localhost_find
      delegate_to: localhost
      run_once: true

    - name: Find in the hosts for comparison
      find:
        path: /test
        file_type: any
      register: host_find

    - name: List /test of localhost
      debug:
        msg: "{{ localhost_find.files | map(attribute='path') | list }}"

    - name: List /test of host
      debug:    
        msg: "{{ host_find.files | map(attribute='path') | list }}"      

    - name: Remove /test folder on localhost
      file:
        path: /test
        state: absent
      delegate_to: localhost
      run_once: true

    - name: Delegate an empty find to localhost
      find:
        path: /test
        file_type: any
      register: empty_find
      delegate_to: localhost
      run_once: true

    - name: Here are our hostnames from the inventory
      debug:
        msg: "{{ inventory_hostname }}"

    - name: I am the evil host killer
      meta: end_host
      when: empty_find.files | count == 0 and inventory_hostname != 'host1' 

    - debug:
        msg: "I am a sad message, because I will never display :'( But hopefully host1 likes me :')"

    - name: I am the evil playbook killer
      meta: end_play
      when: empty_find.files | count == 0

    - debug:
        msg: "I am a sad message, because I will never display :'( No one likes me..."

Where you can see, that, I am skipping the very last debug message by ending the play, when I ended two out of the three hosts I have in my hosts group on the step before.

Output of that playbook:

PLAY [hosts] **************************************************************************************************************************

TASK [Delegate directory creation to localhost] ***************************************************************************************
ok: [host1 -> localhost]

TASK [Create directory on hosts] ****************************************************************************************************
ok: [host3]
ok: [host2]
ok: [host1]

TASK [Delegate file creation to localhost] ********************************************************************************************
changed: [host1 -> localhost]

TASK [Create file on hosts] ****************************************************************************************************
changed: [host2]
changed: [host1]
changed: [host3]

TASK [Delegate a find to localhost] ***************************************************************************************************
ok: [host1 -> localhost]

TASK [Find in the host for comparison] ************************************************************************************************
ok: [host1]
ok: [host3]
ok: [host2]

TASK [List /test of localhost] ********************************************************************************************************
ok: [host1] => {
    "msg": [
        "/test/localhost.txt"
    ]
}
ok: [host2] => {
    "msg": [
        "/test/localhost.txt"
    ]
}
ok: [host3] => {
    "msg": [
        "/test/localhost.txt"
    ]
}

TASK [List /test of host] *************************************************************************************************************
ok: [host1] => {
    "msg": [
        "/test/host.txt"
    ]
}
ok: [host2] => {
    "msg": [
        "/test/host.txt"
    ]
}
ok: [host3] => {
    "msg": [
        "/test/host.txt"
    ]
}

TASK [Remove /test folder on localhost] ***********************************************************************************************
changed: [host1 -> localhost]

TASK [Delegate an empty find to localhost] ********************************************************************************************
ok: [host1 -> localhost]

TASK [Here are our hostnames from the inventory] **************************************************************************************
ok: [host1] => {
    "msg": "host1"
}
ok: [host2] => {
    "msg": "host2"
}
ok: [host3] => {
    "msg": "host3"
}

TASK [debug] **************************************************************************************************************************
ok: [host1] => {
    "msg": "I am a sad message, because I will never display :'( But hopefully host1 likes me :')"
}

PLAY RECAP ****************************************************************************************************************************
host1                      : ok=12   changed=3    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
host2                      : ok=6    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
host3                      : ok=6    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0  

In the output, you can easily spot the delegation, as is will output

changed: [host1 -> localhost]

When a non-delegated task would just go

changed: [host1]

Using hostvars

I would say the approach is a little bit less state of the art, but could become handy if you would like to delegate on edge cases. I have used it before, so it is not like there is no situation where it could not end up has being the right solution.

Here is the example

---
- hosts: localhost
  gather_facts: false

  tasks:
    - name: Find in localhost
      find:
        path: /not/existing/folder
        file_type: any
      register: find

    - name: This will register the list under find.files as a variable on the host, making it accessible via hostvars
      set_fact:
        find_result: "{{ find.files }}"

    - meta: end_play
      when: find.files | count == 0

    - debug:
        msg: I am the sanity check message, proving the end_play did happen


- hosts: hosts
  gather_facts: false

  tasks:
    - name: Just to show we are not cheating with an empty host group, we display the hosts names
      debug:
        msg: "{{ inventory_hostname }}"

    - name: To show it is really our empty list and not an empty string or a null variable
      debug:
        msg: "{{ hostvars['localhost']['find_result'] }}"

    - meta: end_play
      when: "hostvars['localhost']['find_result'] | count == 0"

    - debug:
        msg: I am a first sanity check message, proving the end_play did happen

    - debug:
        msg: I am a second sanity check message, proving the end_play did happen


- hosts: localhost
  gather_facts: false

  tasks:
    - name: To show it is really our empty list and not an empty string or a null variable
      debug:
        msg: "{{ hostvars['localhost']['find_result'] }}"

    - meta: end_play
      when: "hostvars['localhost']['find_result'] | count == 0"

    - debug:
        msg: I am a first sanity check message, proving the end_play did happen

    - debug:
        msg: I am a second sanity check message, proving the end_play did happen

Where you can see that, at the beginning of each new tasks group for a host, I just run the meta to end the play, based on the variable find_result registered out of the find result

Here is the output for this one

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

TASK [Find in localhost] **************************************************************************************************************
ok: [localhost]

TASK [This will register the list under find.files as a variable on the host, making it accessible via hostvars] *********************
ok: [localhost]

PLAY [hosts] **************************************************************************************************************************

TASK [Just to show we are not cheating with an empty host group, we display the hosts names] ******************************************
ok: [host1] => {
    "msg": "host1"
}
ok: [host2] => {
    "msg": "host2"
}
ok: [host3] => {
    "msg": "host3"
}

TASK [To show it is really our empty list and not an empty string or a null variable] ************************************************
ok: [host1] => {
    "msg": []
}
ok: [host2] => {
    "msg": []
}
ok: [host3] => {
    "msg": []
}

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

TASK [To show it is really our empty list and not an empty string or a null variable] ************************************************
ok: [localhost] => {
    "msg": []
}

PLAY RECAP ****************************************************************************************************************************
host1                      : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
host2                      : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
host3                      : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
localhost                  : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   



回答2:


Try using a block that only triggers your second two tasks after you do your first:

https://docs.ansible.com/ansible/latest/user_guide/playbooks_blocks.html

---
- name: Play 1
  hosts: 127.0.0.1
  tasks:
  - name: find the latest file
    find: paths=/var/lib/jenkins/jobs/process/workspace/files
          file_type=file
          age=-1m
          age_stamp=mtime
    register: files

  - name: Play 2 & 3 if Play 1 has a file
    block:
      - name: Play 2
        hosts: all
        serial: 5
        tasks:
          - name: copy latest file
            copy: src=data_init/goldy.init.qa dest=/data01/admin/files/goldy.init.qa owner=golden group=golden

          - name: copy latest file
            copy: src=data_init/goldy.init.qa dest=/data02/admin/files/goldy.init.qa owner=golden group=golden

      - name: Play 3
        hosts: 127.0.0.1
        tasks:
          - name: execute command
            shell: ./data_init --init_file ./goldy.init.qa
    when: files != ""
...


来源:https://stackoverflow.com/questions/58275627/how-to-skip-all-other-plays-in-ansible-playbook-if-some-condition-is-not-met

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!