python - 处理多个子元素的最有效方法是什么?

标签 python ansible jinja2

我正在使用 python 进行用户管理。我有一个“user_groups” map 数组,其中每个元素都包含一个具有 map 名称的键,值是应配置该组的主机数组。

user_groups:
  robots: "test_server1,test_server2"
  developers: "test_server3,test_server4"
  tests: "test_server5"

我还有一个 user_names 数组,其中每个元素都包含一个映射,其中包含诸如该人的用户名、该人应关联的组以及该人的任何其他主机等详细信息。用户应与关联。

user_names:
  - username: mohamed
    group:
      - robots
      - tests
    hosts:
      user:
        - test_server4

到目前为止,我们的 ansible playbook 已在所有主机上运行,​​并且仅当用户通过组或通过每个用户定义的hosts.user 与主机关联时才将用户添加到主机。子元素循环促进了这一点。举个例子:

- name: If user is not associated with this host via groups or item.hosts.user, then ensure user is absent from host and remove user's home directory.
  become: true
  user:
    state: present
    name: "{{ item.0.username }}"
  with_subelements:
    - "{{ user_names }}"
    - group
    - flags:
      skip_missing: true
  when: (item.0.hosts is defined and item.0.hosts.user is defined and inventory_hostname in item.0.hosts.user) or (inventory_hostname in user_groups.{{ item.1 }} ))

这一直工作得很好,但现在我需要确保用户不在其不关联的所有主机上。这里的主要用例是经历 groups/user.hosts 更改的用户,但它们仍然存在于不再关联的服务器上。

如果我尝试上面发布的相同代码块,但反转 when 条件并将 state 更改为 absent,它不会由于循环的性质而工作;当在特定主机上运行任务时,我们循环遍历组中列出的每个服务器。并非每次迭代都会匹配 inventory_hostname,因此 ansible 会先发制人地删除用户,即使该关联在以后的迭代中会匹配。

我想我正在尝试找出仅当循环的所有迭代都匹配条件时如何运行任务。或者也许有一种更优雅的方法来解决问题。

预期结果:

将用户映射到两个组。运行 ansible 以在组中包含的主机上配置用户。

从用户中删除一组。运行 ansible 并期望将用户从不再与其关联的主机中删除。

我尝试过嵌套子元素,使用product jinja过滤器,unions,但似乎无法弄清楚这一点。

感谢您的帮助。

最佳答案

阅读您的评论,我更好地理解您想要做什么。这是一种不同的方法:构建用户应存在的主机的统一列表。

让我们从这个示例数据开始(这次我使用了两个现有变量的字典):

---
- hosts: all
  gather_facts: false
  vars:
    user_groups:
      robots: [host0, host2]
      developers: [host0]
      tests: [host2]

    user_names:
      bob:
        group:
          - robots
          - tests
        hosts:
          - host0
      alice:
        group:
          - developers
        hosts:
          - host1

现在,对于每个用户,我们将为其定义一个统一的主机列表:

---
    # Just set a default value for user_hosts to avoid a bunch of
    # calls to the |default filter in the following expression.
    - set_fact:
        user_hosts: {}

    - set_fact:
        user_hosts: >-
          {{ user_hosts|combine({
          item.key:
          (
          item.value.hosts|default([])
          + user_groups|json_query('[{}][]'.format(','.join(item.value.group)))
          )|unique
          }) }}
      loop: "{{ user_names|dict2items }}"

    - debug:
        var: user_hosts

这将产生:

ok: [host0] => {
    "user_hosts": {
        "alice": [
            "host1", 
            "host0"
        ], 
        "bob": [
            "host0", 
            "host1", 
            "host2"
        ]
    }
}

alice 是在 host1 上定义的,因为它是在 user_names 中显式声明的。她在 host0 上定义,因为她是 developers 组的成员。

bob 最终在本示例中的所有三个主机上定义:host0,因为它是在 user_nameshost1 中显式声明的host2 因为他是 robotstests 组的成员。

获得此列表后,创建和删除用户就很简单:

- name: create users
  debug:
    msg: "create user {{ item.key }}"
  when: inventory_hostname in item.value
  loop: "{{ user_hosts|dict2items }}"
  loop_control:
    label: "{{ item.key }}"

- name: delete users
  debug:
    msg: "delete user {{ item.key }}"
  when: inventory_hostname not in item.value
  loop: "{{ user_hosts|dict2items }}"
  loop_control:
    label: "{{ item.key }}"

更新

如果您的组名称不是有效的标识符(例如,它们有空格或 - 等),则需要在 jmespath 表达式中引用它们:

- set_fact:
    user_hosts: >-
      {{ user_hosts|combine({
      item.key:
      (
      item.value.hosts|default([])
      + user_groups|json_query('[{}][]'.format(
      ','.join(item.value.group|map('regex_replace', '(.*)', '"\1"'))
      ))
      )|unique
      }) }}
  loop: "{{ user_names|dict2items }}"

此处,我们使用 regex_replace 过滤器和 map 在列表中的每个项目周围添加引号。

关于python - 处理多个子元素的最有效方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55938051/

相关文章:

python - 使用 Jinja2 构建链接列表

python - 我可以在 "model.fit()"循环中使用 "for"来更改每次迭代中的训练数据吗

json - 用于展平对象数组的 JMESPath 表达式,每个对象都有嵌套的对象数组

python - 如何访问 Jinja2 模板(应用引擎上的 Bottle 框架)中的 session 数据?

python - webpy 表单可以与 jinja2 配合使用吗?

ansible - 为什么 Ansible 会忽略 delegate_to 参数?

python - Folium:来自 GeoJson 的圆形标记

python - Python中进程执行检查并获取PID

python - SocketServer.ThreadingTCPServer - 程序重启后无法绑定(bind)到地址

ansible - 如何使用 Ansible 模块将 Base64 var 解码为二进制文件