如何匹配/搜索作为列表的dict属性的子字符串



以下是场景:

  • 一个剧本,它调用一个角色在多个服务器中创建用户,包括一个VM规模集(其中无法预测ansible_hostnames(-库存已经在动态生成,工作正常,而不是问题
  • users-dict变量将提供用户列表以及每个用户的一系列属性
  • 其中一个属性是名为targetservers的服务器列表,这个变量的属性才是真正的问题
  • 战术手册使用target_servers来决定用户是否在该特定服务器上——它补充了ansible的库存
  • target_servers可能只包括特定目标主机的起始名称,一个子字符串,如";vmss";作为";vmss*";通配符,但也固定主机名server12345、server12346等
  • 因此,动态清单告诉ansible要连接到哪些服务器,但变量告诉它应该创建用户还是从特定的服务器中删除用户(即服务器有不同的用户(

目标:

有一个条件,检查target_server列表元素内容是否与ansible_hostname匹配(即,如果在target_servers列表中找到的子字符串(来自users dict(匹配,则我们提供用户;另外,当然,如果列表提供了整个主机名,那么它应该匹配,并且还为用户提供

这是代码:

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

我已经尝试过研究selectattrjson_querysubelements,但目前我不了解如何使它们与列表dict属性中的子字符串匹配。

在上面的示例中,通过从is match()更改为in,可以很好地使用确切的主机名,但这不是目标。我需要匹配这些主机名的确切主机名和子字符串。

任何关于如何实现这一点的帮助或关于替代方法的建议都将不胜感激。


如果我能找到一种方法,在遍历整个字典后,根据列表(target_servers(运行它,这里的例子可能会起作用(嵌套循环可能吗?(:https://docs.ansible.com/ansible/latest/user_guide/playbooks_tests.html#testing-字符串

我想我刚刚找到了我需要的东西: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)

您可以使用select过滤器将in测试应用于用户的target_servers列表中的所有元素。

这将是您的调试任务:

- 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

最新更新