Я использую вложенный цикл в Ansible («создайте 3 виртуальные машины для каждого из 10 пользователей»):
- name: Add hosts to inventory
add_host:
name: "{{ '%s-%02d-%02d' | format(vm_prefix, item.0, item.1.number) }}"
groups: vms
loop: "{{userlist | product(vms_per_user) | list }}"
Мой вопрос: есть ли у меня способ получить индекс элемента во втором списке?
- name: Add hosts to inventory
add_host:
name: "{{ '%s-%02d-%02d' | format(vm_prefix, item.0, item.1.number) }}"
groups: vms
vm_index: "{{ get the index of this particular VM in vms_per_user }}"
loop: "{{userlist | product(vms_per_user) | list }}"
Я знаю о with_indexed_items
и flatten + loop_control.index
, но я не могу понять, как это написать, чтобы я получал индекс, который зацикливается только на втором списке и перезапускается с 0 для каждого нового пользователя (каждого нового элемента в первом списке).
TL; DR - я ищу аналог этой конструкции Python:
for user in users:
for (index, vm_name) in enumerate(vms_per_user):
do_something_with user, index, vm_name
Спасибо!
Если бы в Ansible был фильтр enumerate
, это было бы довольно просто. Это не так, но мы можем дать ему один. Я помещаю следующий контент в filter_plugins/enumerate.py
:
#!/usr/bin/python
def filter_enumerate(v):
return list(enumerate(v))
class FilterModule (object):
def filters(self):
return {
'enumerate': filter_enumerate,
}
Для списка [a, b, c]
будет возвращен новый список [[0,a], [1,b], [2,c]]
. Мы можем использовать это в вашей пьесе следующим образом:
---
- hosts: localhost
gather_facts: false
vars:
userlist:
- alice
- bob
- mallory
vms_per_user:
- vm1
- vm2
- vm3
vm_prefix: foo-
tasks:
- debug:
msg:
add_host:
name: "{{ vm_prefix }}{{ item.0 }}-{{ item.1.1 }}"
groups: vms
vm_index: "{{ item.1.0 }}"
loop: "{{ userlist | product(vms_per_user|enumerate) | list }}"
Вывод этой задачи отладки будет выглядеть примерно так:
ok: [localhost] => (item=[u'alice', [0, u'vm1']]) => {
"msg": {
"add_host": {
"groups": "vms",
"name": "foo-alice-vm1",
"vm_index": "0"
}
}
}
ok: [localhost] => (item=[u'alice', [1, u'vm2']]) => {
"msg": {
"add_host": {
"groups": "vms",
"name": "foo-alice-vm2",
"vm_index": "1"
}
}
}
ok: [localhost] => (item=[u'alice', [2, u'vm3']]) => {
"msg": {
"add_host": {
"groups": "vms",
"name": "foo-alice-vm3",
"vm_index": "2"
}
}
}
И т.п.
Рад помочь! Если вас устраивает этот ответ, вы можете поставить галочку слева от вас.
Из любопытства - насколько мне известно, enumerate
возвращает список кортежей. Однако на выходе ansible отображается список списков. Является ли это поведение специфичным для ansible или какой-то особенностью python, о которой я не знаю? Чтобы ответить на ваш другой комментарий - я также приму ответ, как только у меня будет возможность его проверить. Еще раз, спасибо!
Подозреваю, что Jinja просто конвертирует кортежи в списки, но точно не знаю. Я не думаю, что различие имеет смысл в этой ситуации.
Несмотря на то, что ответ larsks совершенно действителен (и я уже несколько месяцев использую его решение в своих сборниках игр - еще раз спасибо за это, larsks!), я только что узнал, что есть другое, более "нативное" решение. к моей проблеме. Поэтому я добавляю это сюда, на случай, если кто-то еще наткнется на проблему в будущем.
В Ansible 2.5 параметр index_var
был введен в loop_control
. (см. документы ). Вы можете использовать это напрямую, без необходимости в дополнительных настраиваемых фильтрах.
Синтаксис выглядит следующим образом:
- name: count our fruit
debug:
msg: "{{ item }} with index {{ my_idx }}"
loop:
- apple
- banana
- pear
loop_control:
index_var: my_idx
Начиная с Ansible 2.8, существуют расширенные циклы, которые добавляют поддержку индексов списков. Проверить https://docs.ansible.com/ansible/latest/user_guide/playbooks_loops.html#extended-loop-variables
В основном добавить:
loop_control:
extended: yes
и используйте ansible_loop.index0
или ansible_loop.index
Спасибо Богду за то, что направил меня в этом направлении.
Вот это вообще гениально! :) Спасибо! На самом деле я искал фильтр
enumerate
, но понятия не имел, как его добавить.