如何在Ansible中基于用户配置向字典列表追加字典
问题:通过Ansible动态生成systemd服务配置列表
场景说明
需要通过Ansible部署一个支持可配置节点数量的应用,每个应用节点以systemd服务运行。使用systemd服务管理角色维护这些服务,该角色要求提供服务名称、优先级等配置信息。
用户在playbook中定义的节点变量如下:
application_nodes: - name: "node1" api_port: 3900 - name: "node2" api_port: 3910
尝试的方法及错误
方法1:字符串拼接生成服务列表
尝试用模板循环拼接字符串生成服务配置,代码如下:
service_list: "{% for node in application_nodes %}{'name': 'application_{{ node.name }}.service', 'priority': 1000}{% endfor %}" devture_systemd_service_manager_services_list_auto: | {{ ([{{ service_list }}]) }}
执行后报错:
FAILED! => {"msg": "An unhandled exception occurred while templating '{{\n ([{{ service_list }}])\n}}\n'. Error was a <class 'ansible.errors.AnsibleError'>, original message: template error while templating string: expected token ':', got '}'. String: {{\n ([{{ service_list }}])\n}}\n. expected token ':', got '}'"}
方法2:循环追加列表到变量
尝试用set_fact循环追加服务配置,代码如下:
- name: Append services list by nodes set_fact: devture_systemd_service_manager_services_list_auto: "{{ devture_systemd_service_manager_services_list_auto + [{'name': 'application_{{ item.name }}.service', 'priority': 1000, 'groups': ['applicationname']}] }}" loop: "{{ application_nodes }}" - name: "Print services" debug: msg: "services {{ devture_systemd_service_manager_services_list_auto }}"
执行后报错:
TASK [custom/base : Print services B] ******************************************************************************************************************************************************************************************************************** fatal: [mydomain]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: [{'name': 'application_node2.service', 'priority': 1000, 'groups': ['applicationname']}, {'name': 'application_{{ item.name }}.service', 'priority': 1000, 'groups': ['applicationname']}]: 'item' is undefined. 'item' is undefined. [{'name': 'application_node2.service', 'priority': 1000, 'groups': ['applicationname']}, {'name': 'application_{{ item.name }}.service', 'priority': 1000, 'groups': ['applicationname']}]: 'item' is undefined. 'item' is undefined\n\nThe error appears to be in '/.../roles/custom/base/tasks/add_services.yml': line 11, column 3, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n- name: \"Print services B\"\n ^ here\n"}
期望的最终效果
希望自动生成适配所有节点的服务列表,效果如下:
devture_systemd_service_manager_services_list_auto: | {{ ([{'name': 'application_node1.service', 'priority': 1000}]) + ([{'name': 'application_node2.service', 'priority': 1000}]) }}
解决方案
方案1:使用列表推导式直接生成
Ansible支持Jinja2列表推导式,可直接生成所需服务列表,无需字符串拼接或循环追加:
devture_systemd_service_manager_services_list_auto: "{{ application_nodes | map('combine', {'name': 'application_' ~ item.name ~ '.service', 'priority': 1000, 'groups': ['applicationname']}) | list }}"
或者更简洁的写法:
devture_systemd_service_manager_services_list_auto: >- {{ application_nodes | map('extract', {}, ['name']) | map('regex_replace', '^(.*)$', 'application_\\1.service') | map('community.general.dict_kv', 'name') | map('combine', {'priority': 1000, 'groups': ['applicationname']}) | list }}
方案2:修复set_fact循环写法
如果坚持用循环追加,需注意变量嵌套语法,避免在双引号中嵌套{{ }},改用Jinja2字符串拼接:
- name: Initialize services list if not exists set_fact: devture_systemd_service_manager_services_list_auto: [] - name: Append services list by nodes set_fact: devture_systemd_service_manager_services_list_auto: "{{ devture_systemd_service_manager_services_list_auto + [{'name': 'application_' ~ item.name ~ '.service', 'priority': 1000, 'groups': ['applicationname']}] }}" loop: "{{ application_nodes }}" - name: "Print services" debug: msg: "services {{ devture_systemd_service_manager_services_list_auto }}"
注意:必须先初始化列表为空,避免变量未定义问题;同时用~进行字符串拼接,而非嵌套{{ }}。
内容的提问来源于stack exchange,提问作者Moanos
相关产品推荐
相关产品推荐

