问题
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:
- Although it is understandable that you tried defining a
hosts: localhostto 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. - If you really want to go this way (using
hosts: localhost), for some reason, then you'll need to save the return of yourfind, 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