尝试动态生成数组Jinja2

时间:2019-11-08 21:12:51

标签: ansible jinja2

我尝试根据给定的类型动态构建一个数组(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 :(得分:0)

以下满足您的要求。重点:

  • 如果可以通过标准的ansible循环,过滤器和模块进行操作,请不要使用冗长的jinja2模板来分配vars
  • 无需初始化空变量,可以使用default filter
  • 动态构造var名称时,您必须使用array notationvars lookup来获取它。

我们在这里:

---
- 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  

答案 1 :(得分:0)

我刚刚升级到V2.9.0的ansible版本确实存在问题,现在的输出就像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) 


答案 2 :(得分:0)

在我的第一个示例中,我首先结合了上面的解决方案(带有查找变量)的数组。 在继续我的项目时,我也需要组合类型字典的子元素。

- 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访问不会返回字典的子元素。

  - 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

是否可以通过查找变量访问字典的子元素?