如何从作为列表的 dict 属性匹配/搜索子字符串
场景如下:
- 一个 playbook 调用一个角色在多个服务器中创建用户,包括 VM 规模集(其中 ansible_hostnames 无法预测) - 库存已经动态生成并且工作正常,而不是
- users dict 变量会出现 的问题提供用户列表以及每个
- 属性的一系列属性是一个名为 target_servers 的服务器列表 - 该变量的属性是实际问题
- target_servers 由剧本使用来决定用户是否将该特定服务器上存在/不存在 - 它补充了 ansible 的清单
- target_servers 可能仅包含特定目标主机的起始名称、子字符串,例如“vmss”作为“vmss*”通配符,但也包括固定主机名 server12345、server12346、等等
- ,动态清单告诉ansible要连接到哪些服务器,但变量告诉它是否应该在该特定服务器上创建或删除用户(即服务器有不同的用户)
目标:
有一个条件来检查 target_server 列表元素内容是否与 ansible_hostname 匹配(即,如果在 target_servers 列表中找到的子字符串(来自用户字典)匹配,那么我们提供用户;另外,当然,如果列表提供了整个主机名,则它应该匹配并且还配置了用户)
这是代码:
---
- hosts: all
become: yes
vars:
users:
user1:
is_sudo: no
is_chrooted: yes
auth_method: hvault
sa_homedir: firstname1lastname1
state: present
target_servers:
- vmss
- ubuntu
user2:
is_sudo: no
is_chrooted: yes
auth_method: hvault
sa_homedir: firstname2lastname2
state: present
target_servers:
- vmss
- ubuntu18
tasks:
- debug:
msg: "{{ ansible_hostname }}"
- debug:
msg: "{{ item.value.target_servers }}"
loop: "{{ lookup('dict', users|default({})) }}"
# This is just to exemplify what I'm trying to achieve as it is not supposed to work
- debug:
msg: "ansible_hostname is in target_servers of {{ item.key }}"
loop: "{{ lookup('dict', users|default({})) }}"
when: ansible_hostname is match(item.value.target_servers)
这是显示 match
字符串测试不能应用于列表的输出(如预期的那样):
TASK [debug] ************************************************************************************************************************************************
ok: [ubuntu18] =>
msg: ubuntu18
TASK [debug] ************************************************************************************************************************************************
ok: [ubuntu18] => (item={'key': 'user1', 'value': {'is_sudo': False, 'is_chrooted': True, 'auth_method': 'hvault', 'sa_homedir': 'firstname1lastname1', 'state': 'present', 'target_servers': ['vmss', 'ubuntu']}}) =>
msg:
- vmss
- ubuntu
ok: [ubuntu18] => (item={'key': 'user2', 'value': {'is_sudo': False, 'is_chrooted': True, 'auth_method': 'hvault', 'sa_homedir': 'firstname2lastname2', 'state': 'present', 'target_servers': ['vmss', 'ubuntu18']}}) =>
msg:
- vmss
- ubuntu18
TASK [debug] ************************************************************************************************************************************************
fatal: [ubuntu18]: FAILED! =>
msg: |-
The conditional check 'ansible_hostname is match(item.value.target_servers)' failed. The error was: Unexpected templating type error occurred on ({% if ansible_hostname is match(item.value.target_servers) %} True {% else %} False {% endif %}): unhashable type: 'list'
The error appears to be in 'test-play-users-core.yml': line 32, column 5, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
- debug:
^ here
已经尝试研究 selectattr
、json_query
和 subelements
但我目前缺乏对如何使它们匹配子字符串的理解在 dict 属性内这是一个清单。
在上面的示例中,通过从 is match()
更改为 in
,精确的主机名可以正常工作,但这不是目标。我需要匹配这些主机名的确切主机名和子字符串。
任何有关如何实现此目的的帮助或有关替代方法的建议将不胜感激。
如果我能找到一种方法在循环遍历整个字典后针对列表(target_servers)运行它(可以嵌套循环吗?),那么这里的示例可能会起作用: https://docs.ansible.com/ansible/latest/user_guide/playbooks_tests.html#testing-strings
我想我刚刚找到了我需要的东西:https://docs.ansible.com/ansible/latest/collections/ansible/builtin/subelements_lookup.html
将尝试尽快提供更新。
更新:是的,子元素有效!这是所需的代码:
- name: test 1
debug:
msg: "{{ item.1 }} matches {{ ansible_hostname }}"
with_subelements:
- "{{ users }}"
- target_servers
when: >
ansible_hostname is match(item.1)
Here's the scenario:
- a playbook that calls a role to create users in multiple servers, including a VM Scale Set (where ansible_hostnames can't be predicted) - inventory is already being dynamically generated and works fine and not the issue
- a users dict variable will provide the user list as well as a series of attributes for each
- one of these attributes is a server list named target_servers - this variable's attribute is the actual issue
- target_servers is used by the playbook to decide if the user will be present/absent on that particular server - it complements ansible's inventory
- target_servers might include only the starting name of a particular target host, a sub-string, like "vmss" as a "vmss*" wildcard, but also fixed hostnames server12345, server12346, etc.
- so, dynamic inventory tells ansible which servers to connect to, but the variable tells it whether the user should be created or removed from that particular servers (i.e. servers have different users)
Objective(s):
Have a conditional that checks if a target_server list element content matches the ansible_hostname (i.e. if the substring found in the target_servers list (from the users dict) matches, then we provision the user; additionally, off course, if the list provides the entire hostname, it should match and the users also be provisioned)
Here's the code:
---
- hosts: all
become: yes
vars:
users:
user1:
is_sudo: no
is_chrooted: yes
auth_method: hvault
sa_homedir: firstname1lastname1
state: present
target_servers:
- vmss
- ubuntu
user2:
is_sudo: no
is_chrooted: yes
auth_method: hvault
sa_homedir: firstname2lastname2
state: present
target_servers:
- vmss
- ubuntu18
tasks:
- debug:
msg: "{{ ansible_hostname }}"
- debug:
msg: "{{ item.value.target_servers }}"
loop: "{{ lookup('dict', users|default({})) }}"
# This is just to exemplify what I'm trying to achieve as it is not supposed to work
- debug:
msg: "ansible_hostname is in target_servers of {{ item.key }}"
loop: "{{ lookup('dict', users|default({})) }}"
when: ansible_hostname is match(item.value.target_servers)
Here's the output showing that the match
string test cannot be applied to a list (as expected):
TASK [debug] ************************************************************************************************************************************************
ok: [ubuntu18] =>
msg: ubuntu18
TASK [debug] ************************************************************************************************************************************************
ok: [ubuntu18] => (item={'key': 'user1', 'value': {'is_sudo': False, 'is_chrooted': True, 'auth_method': 'hvault', 'sa_homedir': 'firstname1lastname1', 'state': 'present', 'target_servers': ['vmss', 'ubuntu']}}) =>
msg:
- vmss
- ubuntu
ok: [ubuntu18] => (item={'key': 'user2', 'value': {'is_sudo': False, 'is_chrooted': True, 'auth_method': 'hvault', 'sa_homedir': 'firstname2lastname2', 'state': 'present', 'target_servers': ['vmss', 'ubuntu18']}}) =>
msg:
- vmss
- ubuntu18
TASK [debug] ************************************************************************************************************************************************
fatal: [ubuntu18]: FAILED! =>
msg: |-
The conditional check 'ansible_hostname is match(item.value.target_servers)' failed. The error was: Unexpected templating type error occurred on ({% if ansible_hostname is match(item.value.target_servers) %} True {% else %} False {% endif %}): unhashable type: 'list'
The error appears to be in 'test-play-users-core.yml': line 32, column 5, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
- debug:
^ here
Already tried researching about selectattr
, json_query
and subelements
but I currently lack the understanding on how to make them work to match a substring inside a dict attribute that is a list.
In the example above, by changing from is match()
to in
, exact hostnames work fine, but that is not the goal. I need to match both exact hostnames and sub-strings for these hostnames.
Any help on how to accomplish this or suggestions about alternate methods will be greatly appreciated.
The example here might work if I could find a way to run it against a list (target_servers) after having already looped through the entire dictionary (are nested loops possible?): https://docs.ansible.com/ansible/latest/user_guide/playbooks_tests.html#testing-strings
I guess I've just found what I needed: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/subelements_lookup.html
Will try and provide an update soon.
Update: yes, subelements work! Here's the code needed:
- name: test 1
debug:
msg: "{{ item.1 }} matches {{ ansible_hostname }}"
with_subelements:
- "{{ users }}"
- target_servers
when: >
ansible_hostname is match(item.1)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您可以使用
选择
< /a> 过滤器以应用in
测试用户的target_servers
列表中的所有元素。这将是您的调试任务:
给定剧本:
这会产生:
使用
子元素
相反:将产生:
You can use the
select
filter to apply thein
test to all the elements of your users'target_servers
list.This would be your debug task:
Given the playbook:
This yields:
Doing it with
subelements
instead:Will yield: