穿越字典列表并重置索引

huangapple go评论58阅读模式
英文:

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=&#39;devices&#39;)|map(&#39;length&#39;) %}
      - index: {{ range(1, l + 1) }}
      {% endfor %}      
    dirs_index: &quot;{{ dirs|zip(index|from_yaml)|map(&#39;combine&#39;) }}&quot;

  tasks:

    - debug:
        var: index
    - debug:
        var: dirs_index|to_yaml

    - debug:
        msg: &quot;{{ item.0.name }}/disk{{ &#39;%02d&#39; % item.1 }}&quot;
      loop: &quot;{{ dirs_index|subelements(&#39;index&#39;) }}&quot;

    - file:
        state: directory
        path: &quot;/tmp/example/{{ item.0.name }}/disk{{ &#39;%02d&#39; % item.1 }}&quot;
      loop: &quot;{{ dirs_index|subelements(&#39;index&#39;) }}&quot;

<hr>

In production, simplify the playbook. Put the expressions into the group_vars/all

shell&gt; cat group_vars/all/dirs_index.yml 
devs_index: |
  {% for l in dirs|map(attribute=&#39;devices&#39;)|map(&#39;length&#39;) %}
  - index: {{ range(1, l + 1) }}
  {% endfor %}  
dirs_index: &quot;{{ dirs|zip(devs_index|from_yaml)|map(&#39;combine&#39;) }}&quot;

Create the playbook

shell&gt; cat pb.yml
- hosts: all

  tasks:

    - file:
        state: directory
        path: &quot;/tmp/example/{{ item.0.name }}/disk{{ &#39;%02d&#39; % item.1 }}&quot;
      loop: &quot;{{ dirs_index|subelements(&#39;index&#39;) }}&quot;

Put the data into vars. For example, create the file

shell&gt; cat dirs.yml 
dirs:
  - {name: aaa, devices: [/dev/sdb, /dev/sdc]}
  - {name: bbb, devices: [/dev/sdf, /dev/sdg]}

Run the playbook

shell&gt; ansible-playbook -e @dirs.yml -l localhost pb.yml

</sup>

huangapple
  • 本文由 发表于 2023年4月13日 23:20:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/76007191.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定