使用 jinja2 创建 ansible 列表变量
Create ansible list variable with jinja2
以下代码在角色的 defaults/main.yml 文件中:
file_env: "{% if cf_env is equalto 'cf10_dev' %}\
dev\
{% elif cf_env is equalto 'cf10_stg' %}\
stg\
{% elif (cf_env is equalto 'cf10_prd') or (cf_env is equalto 'cf10_prd_ext') %}\
prd\
{% elif cf_env is equalto 'cf11' %}\
[dev, prd]
{% endif %}"
前 3 个条件语句工作正常,其中 file_env
var 设置为单个值,但是当尝试将 file_env
var 设置为列表时(最后一个 elif 语句),它不起作用:
failed: [server-new.xxx.com] (item=[dev, prd] ) => {"ansible_loop_var": "item", "changed": false, "item": "[dev, prd] ", "msg": "Destination /opt/coldfusion11/[dev, prd] 01/bin/jvm.config does not exist !", "rc": 257}
这是生成上述错误的任务:
- name: Update jvm.config for coldfusion11 server
lineinfile:
path: /opt/coldfusion11/{{ item }}01/bin/jvm.config
regexp: '^java.home'
line: 'java.home=/usr/lib/jvm/jre-openjdk'
loop:
- "{{ file_env }}"
notify:
- handler_create_script_list
- handler_restart_coldfusion10
when:
- cf_env == "cf11"
如何将 file_env
变量设置为列表?
我认为您的问题是它将变量解释为字符串,字面意思是 '[dev, prd] '
(根据错误消息判断,最后是 space)。
中建议的不同格式
如果您要遍历您的 var,您应该始终 return 一个列表(需要时使用单个元素)以避免可能出现棘手的错误。您只需将 loop
表达式修改为以下内容:
loop: "{{ file_env }}"
您的模板表达式没有使用 white space control,这将在输出中出现不应该出现的新行。
你的变量定义很难读(这让我觉得你有更深层次的项目架构问题,但那是另一回事了)。考虑到上面的第 1 点,我建议您使用字典中的 key/value 映射并在最终 var
中获取相应的值,重构为以下更具可读性、可持续性和工作性的形式
files_by_env
cf10_dev:
- dev
cf10_stg:
- stg
cf10_prd:
- prd
cf10_prd_ext:
- prd
cf11:
- dev
- prd
file_env: "{{ files_by_env[cf_env] }}"
或者,您可以反转 dict 中的定义(即列出每个文件的环境),这稍微不那么冗长,但会导致更复杂的表达式来获得结果
env_by_file:
dev:
- cf10_dev
- cf11
stg:
- cf10_stg
prd:
- cf10_prd
- cf10_prd_ext
- cf11
file_env: "{{ env_by_file | dict2items | selectattr('value', 'contains', cf_env) | map(attribute='key') | list }}"
以下代码在角色的 defaults/main.yml 文件中:
file_env: "{% if cf_env is equalto 'cf10_dev' %}\
dev\
{% elif cf_env is equalto 'cf10_stg' %}\
stg\
{% elif (cf_env is equalto 'cf10_prd') or (cf_env is equalto 'cf10_prd_ext') %}\
prd\
{% elif cf_env is equalto 'cf11' %}\
[dev, prd]
{% endif %}"
前 3 个条件语句工作正常,其中 file_env
var 设置为单个值,但是当尝试将 file_env
var 设置为列表时(最后一个 elif 语句),它不起作用:
failed: [server-new.xxx.com] (item=[dev, prd] ) => {"ansible_loop_var": "item", "changed": false, "item": "[dev, prd] ", "msg": "Destination /opt/coldfusion11/[dev, prd] 01/bin/jvm.config does not exist !", "rc": 257}
这是生成上述错误的任务:
- name: Update jvm.config for coldfusion11 server
lineinfile:
path: /opt/coldfusion11/{{ item }}01/bin/jvm.config
regexp: '^java.home'
line: 'java.home=/usr/lib/jvm/jre-openjdk'
loop:
- "{{ file_env }}"
notify:
- handler_create_script_list
- handler_restart_coldfusion10
when:
- cf_env == "cf11"
如何将 file_env
变量设置为列表?
我认为您的问题是它将变量解释为字符串,字面意思是 '[dev, prd] '
(根据错误消息判断,最后是 space)。
如果您要遍历您的 var,您应该始终 return 一个列表(需要时使用单个元素)以避免可能出现棘手的错误。您只需将
loop
表达式修改为以下内容:loop: "{{ file_env }}"
您的模板表达式没有使用 white space control,这将在输出中出现不应该出现的新行。
你的变量定义很难读(这让我觉得你有更深层次的项目架构问题,但那是另一回事了)。考虑到上面的第 1 点,我建议您使用字典中的 key/value 映射并在最终 var
中获取相应的值,重构为以下更具可读性、可持续性和工作性的形式files_by_env cf10_dev: - dev cf10_stg: - stg cf10_prd: - prd cf10_prd_ext: - prd cf11: - dev - prd file_env: "{{ files_by_env[cf_env] }}"
或者,您可以反转 dict 中的定义(即列出每个文件的环境),这稍微不那么冗长,但会导致更复杂的表达式来获得结果
env_by_file: dev: - cf10_dev - cf11 stg: - cf10_stg prd: - cf10_prd - cf10_prd_ext - cf11 file_env: "{{ env_by_file | dict2items | selectattr('value', 'contains', cf_env) | map(attribute='key') | list }}"