如何 match/search 来自作为列表的 dict 属性的子字符串
How to match/search a substring from a dict attribute that is a list
场景如下:
- 一个调用角色以在多个服务器中创建用户的剧本,包括 VM 规模集(其中 ansible_hostnames 无法预测)- 清单已经动态生成并且工作正常,而不是问题
- 用户字典变量将提供用户列表以及每个用户的一系列属性
- 其中一个属性是名为 target_servers 的服务器列表 - 此变量的属性是实际问题
playbook 使用 - target_servers 来决定用户是否会 present/absent 在该特定服务器上 - 它补充了 ansible 的清单
- target_servers 可能仅包含特定目标主机的起始名称、子字符串,如“vmss”作为“vmss*”通配符,但也包含固定主机名 server12345、server12346 等
- 因此,动态清单告诉 ansible 要连接到哪些服务器,但是变量告诉它是否应该从该特定服务器创建或删除用户(即服务器有不同的用户)
Objective(s):
有条件检查 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)
您可以对用户 target_servers
列表中的所有元素使用 select
filter to apply the in
测试。
这将是您的调试任务:
- debug:
msg: "hostname is in target_servers of {{ item.key }}"
loop: "{{ users | dict2items }}"
loop_control:
label: "{{ item.key }}"
when: >-
item.value.target_servers
| select('in', inventory_hostname)
| length > 0
鉴于剧本:
- hosts: all
gather_facts: false
vars:
_hostname: ubuntu18
users:
user1:
target_servers:
- vmss
- ubuntu
user2:
target_servers:
- vmss
- ubuntu18
tasks:
- debug:
msg: "hostname is in target_servers of {{ item.key }}"
loop: "{{ users | dict2items }}"
loop_control:
label: "{{ item.key }}"
when: >-
item.value.target_servers
| select('in', inventory_hostname)
| length > 0
这产生:
ok: [ubuntu18] => (item=user1) =>
msg: hostname is in target_servers of user1
ok: [ubuntu18] => (item=user2) =>
msg: hostname is in target_servers of user2
用 subelements
代替:
- hosts: all
gather_facts: false
vars:
_hostname: ubuntu18
users:
user1:
target_servers:
- vmss
- ubuntu
user2:
target_servers:
- vmss
- ubuntu18
tasks:
- debug:
msg: "hostname is in target_servers of {{ item.0.key }}"
loop: "{{ users | dict2items | subelements('value.target_servers') }}"
loop_control:
label: "{{ item.0.key }}"
when: item.1 in inventory_hostname
将产生:
skipping: [ubuntu18] => (item=user1)
ok: [ubuntu18] => (item=user1) =>
msg: hostname is in target_servers of user1
skipping: [ubuntu18] => (item=user2)
ok: [ubuntu18] => (item=user2) =>
msg: hostname is in target_servers of user2
场景如下:
- 一个调用角色以在多个服务器中创建用户的剧本,包括 VM 规模集(其中 ansible_hostnames 无法预测)- 清单已经动态生成并且工作正常,而不是问题
- 用户字典变量将提供用户列表以及每个用户的一系列属性
- 其中一个属性是名为 target_servers 的服务器列表 - 此变量的属性是实际问题 playbook 使用
- target_servers 来决定用户是否会 present/absent 在该特定服务器上 - 它补充了 ansible 的清单
- target_servers 可能仅包含特定目标主机的起始名称、子字符串,如“vmss”作为“vmss*”通配符,但也包含固定主机名 server12345、server12346 等
- 因此,动态清单告诉 ansible 要连接到哪些服务器,但是变量告诉它是否应该从该特定服务器创建或删除用户(即服务器有不同的用户)
Objective(s):
有条件检查 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)
您可以对用户 target_servers
列表中的所有元素使用 select
filter to apply the in
测试。
这将是您的调试任务:
- debug:
msg: "hostname is in target_servers of {{ item.key }}"
loop: "{{ users | dict2items }}"
loop_control:
label: "{{ item.key }}"
when: >-
item.value.target_servers
| select('in', inventory_hostname)
| length > 0
鉴于剧本:
- hosts: all
gather_facts: false
vars:
_hostname: ubuntu18
users:
user1:
target_servers:
- vmss
- ubuntu
user2:
target_servers:
- vmss
- ubuntu18
tasks:
- debug:
msg: "hostname is in target_servers of {{ item.key }}"
loop: "{{ users | dict2items }}"
loop_control:
label: "{{ item.key }}"
when: >-
item.value.target_servers
| select('in', inventory_hostname)
| length > 0
这产生:
ok: [ubuntu18] => (item=user1) =>
msg: hostname is in target_servers of user1
ok: [ubuntu18] => (item=user2) =>
msg: hostname is in target_servers of user2
用 subelements
代替:
- hosts: all
gather_facts: false
vars:
_hostname: ubuntu18
users:
user1:
target_servers:
- vmss
- ubuntu
user2:
target_servers:
- vmss
- ubuntu18
tasks:
- debug:
msg: "hostname is in target_servers of {{ item.0.key }}"
loop: "{{ users | dict2items | subelements('value.target_servers') }}"
loop_control:
label: "{{ item.0.key }}"
when: item.1 in inventory_hostname
将产生:
skipping: [ubuntu18] => (item=user1)
ok: [ubuntu18] => (item=user1) =>
msg: hostname is in target_servers of user1
skipping: [ubuntu18] => (item=user2)
ok: [ubuntu18] => (item=user2) =>
msg: hostname is in target_servers of user2