英文:
Going through a list of dictionaries and reset index
问题
I'm struggling with indexes to reset the index of a loop with Ansible.
我在使用Ansible时遇到了循环索引的问题。
I want to create a directory structure like this:
我想创建以下的目录结构:
/aaa/disk01
/aaa/disk02
/bbb/disk01
/bbb/disk02
But I get this:
但我得到了这个:
/aaa/disk01
/aaa/disk02
/bbb/disk03
/bbb/disk04
My variables are set in group_vars
:
我的变量在group_vars
中设置:
dirs:
- { name: "aaa", devices: [ "/dev/sdb", "/dev/sdc" ] }
- { name: "bbb", devices: [ "/dev/sdf", "/dev/sdg" ] }
and my task is
我的任务是:
- name: create device sub-directories
file:
path: "/{{ item.0.name }}/disk0{{ ansible_loop.index }}"
state: directory
loop: "{{ dirs | subelements('devices')}}"
loop_control:
extended: true
So, my question is: how to reset the index after the last device of each main top directory?
因此,我的问题是:如何在每个主顶级目录的最后一个设备之后重置索引?
英文:
I'm struggling with indexes to reset the index of a loop with Ansible.
I want to create a directory structure like this:
/aaa/disk01
/aaa/disk02
/bbb/disk01
/bbb/disk02
But I get this:
/aaa/disk01
/aaa/disk02
/bbb/disk03
/bbb/disk04
My variables are set in group_vars
:
dirs:
- { name: "aaa", devices: [ "/dev/sdb", "/dev/sdc" ] }
- { name: "bbb", devices: [ "/dev/sdf", "/dev/sdg" ] }
and my task is
- name: create device sub-directories
file:
path: "/{{ item.0.name }}/disk0{{ ansible_loop.index }}"
state: directory
loop: "{{ dirs | subelements('devices')}}"
loop_control:
extended: true
So, my question is: how to reset the index after the last device of each main top directory?
答案1
得分: 2
以下是翻译好的内容:
你应该在循环中使用循环,这可以通过在Ansible中使用包含 include_*
关键字来实现,详见文档。所首先,你需要创建一个任务文件来生成目录。我为了调试的目的,将 /
替换为 /var/tmp/example/
。在这里,dir
是外部变量名称,我们将从Playbook传递给包含任务。
create_directories.yaml
- name: 创建设备子目录
ansible.builtin.file:
path: "/var/tmp/example/{{ dir.name }}/disk0{{ ansible_loop.index }}"
state: directory
loop: "{{ dir.devices }}"
loop_control:
extended: true
在下面的Playbook中,我们遍历你的变量列表,将每个映射作为 dir
传递给包含的任务。
playbook.yaml
- hosts: all
name: 循环示例
vars:
dirs:
- { name: "aaa", devices: ["/dev/sdb", "/dev/sdc"]}
- { name: "bbb", devices: ["/dev/sdf", "/dev/sdg"]}
tasks:
- name: 遍历 dirs
ansible.builtin.include_tasks: create_directories.yaml
with_items: "{{ dirs }}"
loop_control:
loop_var: dir
示例输出:
ansible-playbook -i localhost, playbook.yaml
PLAY [循环示例] *************************************************************
TASK [收集信息] **********************************************************
ok: [localhost]
TASK [遍历 dirs] ***********************************************************
included: /home/example/create_directories.yaml for localhost => (item={'name': 'aaa', 'devices': ['/dev/sdb', '/dev/sdc']})
included: /home/example/create_directories.yaml for localhost => (item={'name': 'bbb', 'devices': ['/dev/sdf', '/dev/sdg']})
TASK [创建设备子目录] ********************************************
ok: [localhost] => (item=/dev/sdb)
ok: [localhost] => (item=/dev/sdc)
TASK [创建设备子目录] ********************************************
ok: [localhost] => (item=/dev/sdf)
ok: [localhost] => (item=/dev/sdg)
PLAY RECAP ************************************************************
localhost : ok=5 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
执行此Playbook后,将生成以下目录结构
# tree /var/tmp/example
/var/tmp/example
├── aaa
│ ├── disk01
│ └── disk02
└── bbb
├── disk01
└── disk02
英文:
You should use loop in loop, which is achieved in ansible by using include_* keyword, see docs. So first, you have to create a task file which generates directories. I replaced /
with /var/tmp/example/
for debugging purpose. Here dir
- is outer variable name, that we will pass from playbook to included task
create_directories.yaml
- name: Create device sub-directories
ansible.builtin.file:
path: "/var/tmp/example/{{ dir.name }}/disk0{{ ansible_loop.index }}"
state: directory
loop: "{{ dir.devices }}"
loop_control:
extended: true
In playbook below we iterate over your list of variables, and pass each map as dir
to included tasks
playbook.yaml
- hosts: all
name: Loop example
vars:
dirs:
- { name: "aaa", devices: ["/dev/sdb", "/dev/sdc"]}
- { name: "bbb", devices: ["/dev/sdf", "/dev/sdg"]}
tasks:
- name: Loop over dirs
ansible.builtin.include_tasks: create_directories.yaml
with_items: "{{ dirs }}"
loop_control:
loop_var: dir
Example output:
ansible-playbook -i localhost, playbook.yaml
PLAY [Loop example] *************************************************************
TASK [Gathering Facts] **********************************************************
ok: [localhost]
TASK [Loop over dirs] ***********************************************************
included: /home/example/create_directories.yaml for localhost => (item={'name': 'aaa', 'devices': ['/dev/sdb', '/dev/sdc']})
included: /home/example/create_directories.yaml for localhost => (item={'name': 'bbb', 'devices': ['/dev/sdf', '/dev/sdg']})
TASK [Create device sub-directories] ********************************************
ok: [localhost] => (item=/dev/sdb)
ok: [localhost] => (item=/dev/sdc)
TASK [Create device sub-directories] ********************************************
ok: [localhost] => (item=/dev/sdf)
ok: [localhost] => (item=/dev/sdg)
PLAY RECAP **********************************************************************
localhost : ok=5 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
After executing this playbook, the following directory structure will be generated
# tree /var/tmp/example
/var/tmp/example
├── aaa
│   ├── disk01
│   └── disk02
└── bbb
├── disk01
└── disk02
答案2
得分: 2
重要声明:此解决方案有一个注意事项,你的 devices
子列表不能包含相同的元素。例如:
dirs:
- name: aaa
devices:
- /dev/sdb
- /dev/sdc
- /dev/sdb # 注意这里有重复的元素
- name: bbb
devices:
- /dev/sdf
- /dev/sdg
会产生一个虚假的结果:
/aaa/disk01
/aaa/disk02
/aaa/disk01 ## /!\ 这是虚假的
/bbb/disk01
/bbb/disk02
因为 /dev/sdb
在子列表中出现两次,而索引将返回它在列表中的第一个出现位置。
你可以使用 Python 列表的 index()
方法来找到 devices
列表中子元素的位置。
你还应该考虑使用 format
过滤器 来更合适地填充你的数字为带有零的格式。
因此,使用这个想法,任务将如下所示:
- name: 创建设备子目录
file:
path: >-
/{{ item.0.name }}/disk
{{- "%02d" | format(item.0.devices.index(item.1) + 1) }}
state: directory
loop: "{{ dirs | subelements('devices')}}"
给定这个 debug
任务的例子:
- debug:
msg: >-
/{{ item.0.name }}/disk
{{- "%02d" | format(item.0.devices.index(item.1) + 1) }}
loop: "{{ dirs | subelements('devices')}}"
loop_control:
label: "{{ item.1 }}"
vars:
dirs:
- name: aaa
devices:
- /dev/sdb
- /dev/sdc
- name: bbb
devices:
- /dev/sdf
- /dev/sdg
它会产生以下结果:
ok: [localhost] => (item=/dev/sdb) =>
msg: /aaa/disk01
ok: [localhost] => (item=/dev/sdc) =>
msg: /aaa/disk02
ok: [localhost] => (item=/dev/sdf) =>
msg: /bbb/disk01
ok: [localhost] => (item=/dev/sdg) =>
msg: /bbb/disk02
英文:
Important disclaimer: this solution has a caveat, your devices
sub-lists cannot contain twice the same element.
E.g.:
dirs:
- name: aaa
devices:
- /dev/sdb
- /dev/sdc
- /dev/sdb # mind the duplicate added here
- name: bbb
devices:
- /dev/sdf
- /dev/sdg
Will produce a bogus
/aaa/disk01
/aaa/disk02
/aaa/disk01 ## /!\ this is bogus
/bbb/disk01
/bbb/disk02
Because /dev/sdb
is present twice in the sub-list and the index will return you the first occurrence of it in the list.
You could use the index()
method of a Python list to find back the position of a subelement in the devices
list.
You should also consider using the format
filter to pad your number with zero more properly.
So, here would be the task using this idea:
- name: Create device sub-directories
file:
path: >-
/{{ item.0.name }}/disk
{{- "%02d" | format(item.0.devices.index(item.1) + 1) }}
state: directory
loop: "{{ dirs | subelements('devices')}}"
Given this debug
task as example:
- debug:
msg: >-
/{{ item.0.name }}/disk
{{- "%02d" | format(item.0.devices.index(item.1) + 1) }}
loop: "{{ dirs | subelements('devices')}}"
loop_control:
label: "{{ item.1 }}"
vars:
dirs:
- name: aaa
devices:
- /dev/sdb
- /dev/sdc
- name: bbb
devices:
- /dev/sdf
- /dev/sdg
It would yield:
ok: [localhost] => (item=/dev/sdb) =>
msg: /aaa/disk01
ok: [localhost] => (item=/dev/sdc) =>
msg: /aaa/disk02
ok: [localhost] => (item=/dev/sdf) =>
msg: /bbb/disk01
ok: [localhost] => (item=/dev/sdg) =>
msg: /bbb/disk02
答案3
得分: 1
以下是您要翻译的部分:
Create the list of indexes
index: |
{% for l in dirs|map(attribute='devices')|map('length') %}
- index: {{ range(1, l + 1) }}
{% endfor %}
将生成:
index: |-
- index: [1, 2]
- index: [1, 2]
和合并列表中的项目:
dirs_index: "{{ dirs|zip(index|from_yaml)|map('combine') }}"
将生成:
dirs_index:
- devices: [/dev/sdb, /dev/sdc]
index: [1, 2]
name: aaa
- devices: [/dev/sdf, /dev/sdg]
index: [1, 2]
name: bbb
测试迭代:
- debug:
msg: "{{ item.0.name }}/disk{{ '%02d' % item.1 }}"
loop: "{{ dirs_index|subelements('index') }}"
将得到(缩写):
msg: aaa/disk01
msg: aaa/disk02
msg: bbb/disk01
msg: bbb/disk02
创建目录:
- file:
state: directory
path: "/tmp/example/{{ item.0.name }}/disk{{ '%02d' % item.1 }}"
loop: "{{ dirs_index|subelements('index') }}"
以上的内容是您所需的翻译部分。
英文:
Create the list of indexes
index: |
{% for l in dirs|map(attribute='devices')|map('length') %}
- index: {{ range(1, l + 1) }}
{% endfor %}
gives
index: |-
- index: [1, 2]
- index: [1, 2]
and combine the items on the lists
dirs_index: "{{ dirs|zip(index|from_yaml)|map('combine') }}"
gives
dirs_index:
- devices: [/dev/sdb, /dev/sdc]
index: [1, 2]
name: aaa
- devices: [/dev/sdf, /dev/sdg]
index: [1, 2]
name: bbb
Test the iteration
- debug:
msg: "{{ item.0.name }}/disk{{ '%02d' % item.1 }}"
loop: "{{ dirs_index|subelements('index') }}"
gives (abridged)
msg: aaa/disk01
msg: aaa/disk02
msg: bbb/disk01
msg: bbb/disk02
Create the directories
- file:
state: directory
path: "/tmp/example/{{ item.0.name }}/disk{{ '%02d' % item.1 }}"
loop: "{{ dirs_index|subelements('index') }}"
shell> tree /tmp/example/
/tmp/example/
├── aaa
│   ├── disk01
│   └── disk02
└── bbb
├── disk01
└── disk02
6 directories, 0 files
<hr>
<sup>
Example of a complete playbook for testing
- hosts: localhost
vars:
dirs:
- {name: aaa, devices: [/dev/sdb, /dev/sdc]}
- {name: bbb, devices: [/dev/sdf, /dev/sdg]}
index: |
{% for l in dirs|map(attribute='devices')|map('length') %}
- index: {{ range(1, l + 1) }}
{% endfor %}
dirs_index: "{{ dirs|zip(index|from_yaml)|map('combine') }}"
tasks:
- debug:
var: index
- debug:
var: dirs_index|to_yaml
- debug:
msg: "{{ item.0.name }}/disk{{ '%02d' % item.1 }}"
loop: "{{ dirs_index|subelements('index') }}"
- file:
state: directory
path: "/tmp/example/{{ item.0.name }}/disk{{ '%02d' % item.1 }}"
loop: "{{ dirs_index|subelements('index') }}"
<hr>
In production, simplify the playbook. Put the expressions into the group_vars/all
shell> cat group_vars/all/dirs_index.yml
devs_index: |
{% for l in dirs|map(attribute='devices')|map('length') %}
- index: {{ range(1, l + 1) }}
{% endfor %}
dirs_index: "{{ dirs|zip(devs_index|from_yaml)|map('combine') }}"
Create the playbook
shell> cat pb.yml
- hosts: all
tasks:
- file:
state: directory
path: "/tmp/example/{{ item.0.name }}/disk{{ '%02d' % item.1 }}"
loop: "{{ dirs_index|subelements('index') }}"
Put the data into vars. For example, create the file
shell> cat dirs.yml
dirs:
- {name: aaa, devices: [/dev/sdb, /dev/sdc]}
- {name: bbb, devices: [/dev/sdf, /dev/sdg]}
Run the playbook
shell> ansible-playbook -e @dirs.yml -l localhost pb.yml
</sup>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论