英文:
How to dynamically end an include_tasks loop in Ansible
问题
由于在Ansible中我们无法在块上运行循环,因此我必须在引用的文件包含多个任务的循环中使用include_tasks
。
如果我需要在循环中动态提前结束循环(比如说在loop:
语句中定义的10次循环中提前结束5次),尝试使用when:
条件,但这只能接受预定义/静态定义的变量。如果我需要在每次迭代中更新在when:
中进行评估的变量,我必须在此之前在任务中使用register:
或set_fact:
,但当循环任务正在运行时这是不可能的...
此外,在included_tasks
中,我可以设置meta: end_play
来结束循环和播放执行,但这不是我想要的,播放应该在循环后继续。
英文:
Due to the fact that we cannot run loop on block with Ansible, therefore I have to do include_tasks
in the loop where the referenced file contains more than 1 task.
What if I need to dynamically end the loop earlier (lets say 5 iterations instead of 10 as defined by the loop:
statements). trying to use when:
condition but that only take in pre-defined/statically defined variables, if I have to update the variable that will be evaluated in when:
for each iteration, I have to do register:
or set_fact
in a task before that, which is not possible when the loop task is running...
Also in the included_tasks
I can set meta: end_play
to end the loop and the play execution but that is not what I want, the play should continue after the loop.
答案1
得分: 2
你无法结束它。你只能跳过剩下的部分。例如,
```yaml
- command: "echo {{ item }}"
with_sequence: end=5
register: out
when: out.stdout|d(0)|int < 3
在本地运行时会得到:
changed: [localhost] => (item=1)
changed: [localhost] => (item=2)
changed: [localhost] => (item=3)
skipping: [localhost] => (item=4)
skipping: [localhost] => (item=5)
参见:
include_tasks
include_tasks 的情况相当复杂。ignore_conditional 属性说明:
> 该操作不受条件执行的影响,因此将忽略 when: 关键字
你不能在 include_tasks 中使用 when,但可以将关键字(包括 when)应用于包含的任务。例如,创建文件:
shell> cat tasks_1.yml
- name: Display item
debug:
var: item
- name: Execute command
command: "echo {{ item }}"
register: out
- name: Display stdout
debug:
var: out.stdout
当你包含这个文件时:
- include_tasks:
file: tasks_1.yml
apply:
when: out.stdout|d(0)|int < 3
with_sequence: end=5
你会将条件应用于“包含的任务”。实际上,这将使包含的任务看起来像下面这样:
- name: Display item
debug:
var: item
when: out.stdout|d(0)|int < 3
- name: Execute command
command: "echo {{ item }}"
register: out
when: out.stdout|d(0)|int < 3
- name: Display stdout
debug:
var: out.stdout
when: out.stdout|d(0)|int < 3
运行下面的任务:
- hosts: localhost
tasks:
- include_tasks:
file: tasks_1.yml
apply:
when: out.stdout|d(0)|int < 3
with_sequence: end=5
会得到(已缩减):
TASK [include_tasks] **************************************************************************
included: /export/scratch/tmp7/test-389/tasks_1.yml for localhost => (item=1)
included: /export/scratch/tmp7/test-389/tasks_1.yml for localhost => (item=2)
included: /export/scratch/tmp7/test-389/tasks_1.yml for localhost => (item=3)
included: /export/scratch/tmp7/test-389/tasks_1.yml for localhost => (item=4)
included: /export/scratch/tmp7/test-389/tasks_1.yml for localhost => (item=5)
TASK [Display item] ***************************************************************************
ok: [localhost] =>
item: '1'
TASK [Execute command] ************************************************************************
changed: [localhost]
TASK [Display stdout] *************************************************************************
ok: [localhost] =>
out.stdout: '1'
TASK [Display item] ***************************************************************************
ok: [localhost] =>
item: '2'
TASK [Execute command] ************************************************************************
changed: [localhost]
TASK [Display stdout] *************************************************************************
ok: [localhost] =>
out.stdout: '2'
TASK [Display item] ***************************************************************************
ok: [localhost] =>
item: '3'
TASK [Execute command] ************************************************************************
changed: [localhost]
TASK [Display stdout] *************************************************************************
skipping: [localhost]
TASK [Display item] ***************************************************************************
skipping: [localhost]
TASK [Execute command] ************************************************************************
skipping: [localhost]
TASK [Display stdout] *************************************************************************
ok: [localhost] =>
out.stdout: VARIABLE IS NOT DEFINED!
TASK [Display item] ***************************************************************************
ok: [localhost] =>
item: '5'
TASK [Execute command] ************************************************************************
changed: [localhost]
TASK [Display stdout] *************************************************************************
skipping: [localhost]
你可以看到所有的迭代都会一次性包含,然后依次执行。结果看起来很好,直到 item: '4'
。解释总是取决于变量 out.stdout 在任务执行之前的当前值:
-
[ok] [Display item] item: '3'; out.stdout=2
-
[changed] [Execute command] out.stdout=2
-
[skipping] [Display stdout] out.stdout=3
-
[skipping] [Display item] out.stdout=3
-
[skipping] [Execute command] out.stdout=3
-
[ok] [Display stdout] out.stdout VARIABLE IS NOT DEFINED!
(因为在前一个任务的注册变量 out 中没有 stdout 属性,所以将应用 default(0)) -
[ok] [Display item] item: '5'; default(0)
-
[changed] [Execute command] default(0)
-
[skipping] [Display stdout] out.stdout=5
这是无法使用的。
你可以通过创建一个名为 condition 的变量来改进这个框架。例如,将任务放入一个块中,并始终设置条件:
shell> cat tasks_2.yml
- block:
- name: Execute command {{ item }}
command: "echo {{ item }}"
register: out
- debug:
var: out.stdout
always:
- set_fact:
condition: "{{ out.stdout|int < 3 }}"
运行下面的任务:
- hosts: localhost
tasks:
- include_tasks:
file: tasks_2.yml
apply:
when: condition|d(true)
with_sequence: end=5
会得到(已缩减):
TASK [include_tasks] **************************************************************************
included: /export/scratch/tmp7/test-389/tasks_2.yml for localhost => (item=1
<details>
<summary>英文:</summary>
You can't end it. You can only skip the rest. For example,
```yaml
- command: "echo {{ item }}"
with_sequence: end=5
register: out
when: out.stdout|d(0)|int < 3
gives (on localhost)
changed: [localhost] => (item=1)
changed: [localhost] => (item=2)
changed: [localhost] => (item=3)
skipping: [localhost] => (item=4)
skipping: [localhost] => (item=5)
See:
<hr>
include_tasks
The include_tasks situation is quite complex. The attribute ignore_conditional says:
> The action is not subject to conditional execution so it will ignore the when: keyword
You can't use when with include_tasks, but you can apply keywords, including the keyword when, to the tasks within the include. For example, create the file
shell> cat tasks_1.yml
- name: Display item
debug:
var: item
- name: Execute command
command: "echo {{ item }}"
register: out
- name: Display stdout
debug:
var: out.stdout
When you include this file
- include_tasks:
file: tasks_1.yml
apply:
when: out.stdout|d(0)|int < 3
with_sequence: end=5
you apply the condition "to the tasks within the include". Effectively, this will make the included tasks look like below
- name: Display item
debug:
var: item
when: out.stdout|d(0)|int < 3
- name: Execute command
command: "echo {{ item }}"
register: out
when: out.stdout|d(0)|int < 3
- name: Display stdout
debug:
var: out.stdout
when: out.stdout|d(0)|int < 3
Run the play below
- hosts: localhost
tasks:
- include_tasks:
file: tasks_1.yml
apply:
when: out.stdout|d(0)|int < 3
with_sequence: end=5
gives (abridged)
TASK [include_tasks] **************************************************************************
included: /export/scratch/tmp7/test-389/tasks_1.yml for localhost => (item=1)
included: /export/scratch/tmp7/test-389/tasks_1.yml for localhost => (item=2)
included: /export/scratch/tmp7/test-389/tasks_1.yml for localhost => (item=3)
included: /export/scratch/tmp7/test-389/tasks_1.yml for localhost => (item=4)
included: /export/scratch/tmp7/test-389/tasks_1.yml for localhost => (item=5)
TASK [Display item] ***************************************************************************
ok: [localhost] =>
item: '1'
TASK [Execute command] ************************************************************************
changed: [localhost]
TASK [Display stdout] *************************************************************************
ok: [localhost] =>
out.stdout: '1'
TASK [Display item] ***************************************************************************
ok: [localhost] =>
item: '2'
TASK [Execute command] ************************************************************************
changed: [localhost]
TASK [Display stdout] *************************************************************************
ok: [localhost] =>
out.stdout: '2'
TASK [Display item] ***************************************************************************
ok: [localhost] =>
item: '3'
TASK [Execute command] ************************************************************************
changed: [localhost]
TASK [Display stdout] *************************************************************************
skipping: [localhost]
TASK [Display item] ***************************************************************************
skipping: [localhost]
TASK [Execute command] ************************************************************************
skipping: [localhost]
TASK [Display stdout] *************************************************************************
ok: [localhost] =>
out.stdout: VARIABLE IS NOT DEFINED!
TASK [Display item] ***************************************************************************
ok: [localhost] =>
item: '5'
TASK [Execute command] ************************************************************************
changed: [localhost]
TASK [Display stdout] *************************************************************************
skipping: [localhost]
You can see that all iterations are included at once and then executed serially. The results look fine until item: '4'
. The explanation always depends on the current value of the variable out.stdout before the execution of a task:
-
[ok] [Display item] item: '3'; out.stdout=2
-
[changed] [Execute command] out.stdout=2
-
[skipping] [Display stdout] out.stdout=3
-
[skipping] [Display item] out.stdout=3
-
[skipping] [Execute command] out.stdout=3
-
[ok] [Display stdout] out.stdout VARIABLE IS NOT DEFINED!
(default(0) will apply because there is no attribute stdout in the registered
variable out from the previous task) -
[ok] [Display item] item: '5'; default(0)
-
[changed] [Execute command] default(0)
-
[skipping] [Display stdout] out.stdout=5
This is unusable.
<hr>
You can improve the framework by creating a variable condition. For example, put the tasks into a block and always set the condition
shell> cat tasks_2.yml
- block:
- name: Execute command {{ item }}
command: "echo {{ item }}"
register: out
- debug:
var: out.stdout
always:
- set_fact:
condition: "{{ out.stdout|int < 3 }}"
Run the play below
- hosts: localhost
tasks:
- include_tasks:
file: tasks_2.yml
apply:
when: condition|d(true)
with_sequence: end=5
gives (abridged)
TASK [include_tasks] **************************************************************************
included: /export/scratch/tmp7/test-389/tasks_2.yml for localhost => (item=1)
included: /export/scratch/tmp7/test-389/tasks_2.yml for localhost => (item=2)
included: /export/scratch/tmp7/test-389/tasks_2.yml for localhost => (item=3)
included: /export/scratch/tmp7/test-389/tasks_2.yml for localhost => (item=4)
included: /export/scratch/tmp7/test-389/tasks_2.yml for localhost => (item=5)
TASK [Execute command 1] **********************************************************************
changed: [localhost]
TASK [debug] **********************************************************************************
ok: [localhost] =>
out.stdout: '1'
TASK [set_fact] *******************************************************************************
ok: [localhost]
TASK [Execute command 2] **********************************************************************
changed: [localhost]
TASK [debug] **********************************************************************************
ok: [localhost] =>
out.stdout: '2'
TASK [set_fact] *******************************************************************************
ok: [localhost]
TASK [Execute command 3] **********************************************************************
changed: [localhost]
TASK [debug] **********************************************************************************
ok: [localhost] =>
out.stdout: '3'
TASK [set_fact] *******************************************************************************
ok: [localhost]
TASK [Execute command 4] **********************************************************************
skipping: [localhost]
TASK [debug] **********************************************************************************
skipping: [localhost]
TASK [set_fact] *******************************************************************************
skipping: [localhost]
TASK [Execute command 5] **********************************************************************
skipping: [localhost]
TASK [debug] **********************************************************************************
skipping: [localhost]
TASK [set_fact] *******************************************************************************
skipping: [localhost]
This seems reasonable.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论