Попробуйте динамически сгенерировать массив Jinja2 - PullRequest
1 голос
/ 09 ноября 2019

Я пытаюсь динамически построить массив (fe_components, который я инициализирую как пустой) в зависимости от заданных типов - которые берутся из файла конфигурации в реальной жизни.

Массив для каждого данного типа также происходит из конфигурациифайл.

Чтобы упростить свой экзамен, я записал все переменные в одной игре.

Я перебираю типы и создаю имя соответствующего массива, содержимое которого я хотел бы связатьвсе вместе.

- hosts : all

  vars:

     # types and fe_components_XX come in real life from different config files
     types:
       - rs
       - gg


     fe_components_gg:
         - gg_frontend'

     fe_components_rs:
         - rs_frontend_1
         - rs_frontend_2
         - storybook

     # init empty array to dynamically fill in
     fe_components: []

  tasks:

   # This is what I want to get (written in a static statement)
   - debug: msg="{{ fe_components_rs + fe_components_gg }}"

   # My dynamic approach fails:
   # try to dynamically build up the array for given types
   - set_fact:
       my_dyn_var: >-
          {% for item in types -%}
             {% set varname = 'fe_components_' ~ item -%}
             {{  fe_components + varname  }}
          {% endfor -%}


   - name: test it
     debug:
          msg: " {{ my_dyn_var }}"

Когда я запускаю его, моя конкатенация заканчивается в "может только конкатенировать список (не \" unicode \ ") в список" сообщение.

fatal: [frank-lap]: FAILED! => {"msg": "Unexpected templating type error occurred on ({% for item in types -%}\n   {% set varname = 'fe_components_' ~ item -%}\n   {{  fe_components + varname  }}\n{% endfor -%}): can only concatenate list (not \"unicode\") to list"}

Что я делаю не так?

Ответы [ 3 ]

0 голосов
/ 09 ноября 2019

Это была действительно проблема с доступной версией, которую я только что обновил до V2.9.0, и теперь вывод похож на Zeitounator.

Я знаю, что мое состояние также работает:

  - name: Build my list dynamically from typed list
      vars:
        list_name: "fe_components_{{ item }}"
      set_fact:
        my_dyn_var : "{{ my_dyn_var | default([]) + lookup('vars', list_name) }}"
      loop: "{{ types }}"
      loop_control:
        label: "{{ list_name }}"
      when: vars[list_name] is defined


TASK [Build my list dynamically from typed list] ***********************************************************************************************************************************************
ok: [localhost] => (item=fe_components_rs)
ok: [localhost] => (item=fe_components_gg)
skipping: [localhost] => (item=fe_components_notexists) 


0 голосов
/ 12 ноября 2019

В моем первом примере я начал с объединения массивов, для которых вышеупомянутое решение (с поисковыми переменными) работает довольно хорошо. Продолжая работу над моим проектом, мне тоже нужно объединить подэлементы типизированных словарей.

- name: My test play
  hosts : localhost
  gather_facts: false

  vars:
     types:
       - rs
       - gg
       - notexists

     # backend
     be_components: {}
     solr_cores: {}

     backend_config_rs:
        be_components :
          rs-document:   { dbaccess: True,  version: latest,  mvn_id: rs-document }
          rs-attachments:{ dbaccess: True,  version: latest,  mvn_id: rs-attachments }
        cores:
          rs: { name: rs_core, dir:  /var/solr/data/jurisdict }       


     backend_config_gg:
        be_components :
          gg-document:   { dbaccess: True,  version: latest,  mvn_id: gg-document }
          gg-importer:   { dbaccess: True,  version: latest,  mvn_id: gg-importer }
        cores:
          rs: { name: gg_core, dir:  /var/solr/data/law }      

  tasks:

  # Static demo of what I currently do
  - name: "dyn-config | combine backend variables with RS backend config"
    set_fact: 
       be_components: "{{ be_components | combine ( backend_config_rs.be_components ) }}"
       solr_cores:    "{{ solr_cores | combine ( backend_config_rs.cores ) }}"
    when: "backend_config_rs is defined"

  - name: "dyn-config | combine backend variables with GG backend config"
    set_fact: 
       be_components: "{{ be_components | combine ( backend_config_gg.be_components ) }}"
       solr_cores:    "{{ solr_cores | combine ( backend_config_gg.cores ) }}"
    when: "backend_config_gg is defined"

  - debug: var=be_components
  - debug: var=solr_cores

Но затем я пытаюсь сделать это динамически, я обнаружил, что доступ к поиску vars не возвращает подэлементы dicts.

  - name: "try with lookup"
    vars:
        listname:   "backend_config_{{ item }}"    
    set_fact:
       be_components: "{{ be_components | combine (lookup('vars', listname.components)) }}"
    loop: "{{ types }}"
    loop_control:
        label: "{{ listname }}"       
    when: vars[listname] is defined       

Я получил

FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'ansible.utils.unsafe_proxy.AnsibleUnsafeText object' has no attribute 'components'\n

Есть ли способ получить доступ к подэлементам диктов через переменные поиска?

0 голосов
/ 09 ноября 2019

Следующее удовлетворяет вашим требованиям. Ключевые моменты:

  • Не используйте длинные шаблоны jinja2 для назначения переменных, когда это возможно сделать со стандартными анциклическими циклами, фильтрами и модулями
  • Нет необходимости инициализировать пустые переменные,Вы можете использовать default фильтр
  • Когда вы динамически создаете имя переменной, вам нужно либо использовать нотацию массива , либо поиск vars чтобы получить.

Вот и мы:

---
- name: My test play
  hosts : localhost
  gather_facts: false

  vars:
     types:
       - rs
       - gg
       - notexists

     fe_components_gg:
         - gg_frontend

     fe_components_rs:
         - rs_frontend_1
         - rs_frontend_2
         - storybook

  tasks:

    - name: Static demo of what we are looking for
      debug:
        msg: "{{ fe_components_rs + fe_components_gg }}"

    - name: Build my list dynamically from typed list
      vars:
        list_name: "fe_components_{{ item }}"
      set_fact:
        my_dyn_var : "{{ my_dyn_var | default([]) + lookup('vars', list_name, default=[]) }}"
      loop: "{{ types }}"
      loop_control:
        label: "{{ list_name }}"

    - name: Show my dynamic var
      debug:
        var: my_dyn_var

И результат:

PLAY [My test play] *****************************************************************************************************************************************************************************************************************************************************

TASK [Static demo of what we are looking for] ***************************************************************************************************************************************************************************************************************************
ok: [localhost] => {
    "msg": [
        "rs_frontend_1",
        "rs_frontend_2",
        "storybook",
        "gg_frontend"
    ]
}

TASK [Build my list dynamically from typed list] ************************************************************************************************************************************************************************************************************************
ok: [localhost] => (item=fe_components_rs)
ok: [localhost] => (item=fe_components_gg)
ok: [localhost] => (item=fe_components_notexist)

TASK [Show my dynamic var] **********************************************************************************************************************************************************************************************************************************************
ok: [localhost] => {
    "my_dyn_var": [
        "rs_frontend_1",
        "rs_frontend_2",
        "storybook",
        "gg_frontend"
    ]
}

PLAY RECAP **************************************************************************************************************************************************************************************************************************************************************
localhost                  : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0  
...