如何 match/search 来自作为列表的 dict 属性的子字符串

How to match/search a substring from a dict attribute that is a list

场景如下:

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

已经尝试研究 selectattrjson_querysubelements,但我目前不了解如何让它们匹配列表中的 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