【问题标题】:Ansible: Build reverse dictionary with duplicate values in the original dictionaryAnsible:使用原始字典中的重复值构建反向字典
【发布时间】:2020-11-28 03:20:22
【问题描述】:

我有一个 Ansible 字典事实 vm_templates 看起来像这样:

 {
        "HostedEngine": "00000000-0000-0000-0000-000000000000", 
        "cpu-node0": "2d826ed8-1dbe-4b10-93a1-5ac9734462cb", 
        "cpu-node1": "2d826ed8-1dbe-4b10-93a1-5ac9734462cb", 
        "cpu-node2": "2d826ed8-1dbe-4b10-93a1-5ac9734462cb", 
        "cpu-node3": "2d826ed8-1dbe-4b10-93a1-5ac9734462cb", 
        "fp_gpu-node0": "a08901c0-50a5-4e2f-b6d7-b11b69a32613", 
        "fp_gpu-node1": "a08901c0-50a5-4e2f-b6d7-b11b69a32613", 
        "fp_gpu-node10": "a08901c0-50a5-4e2f-b6d7-b11b69a32613", 
        "fp_gpu-node11": "a08901c0-50a5-4e2f-b6d7-b11b69a32613", 
        "fp_gpu-node2": "a08901c0-50a5-4e2f-b6d7-b11b69a32613", 
        "fp_gpu-node3": "a08901c0-50a5-4e2f-b6d7-b11b69a32613", 
        "fp_gpu-node4": "a08901c0-50a5-4e2f-b6d7-b11b69a32613", 
        "fp_gpu-node5": "a08901c0-50a5-4e2f-b6d7-b11b69a32613", 
        "fp_gpu-node6": "a08901c0-50a5-4e2f-b6d7-b11b69a32613", 
        "fp_gpu-node7": "a08901c0-50a5-4e2f-b6d7-b11b69a32613", 
        "fp_gpu-node8": "a08901c0-50a5-4e2f-b6d7-b11b69a32613", 
        "fp_gpu-node9": "a08901c0-50a5-4e2f-b6d7-b11b69a32613", 
        "hp_gpu-node0": "a08901c0-50a5-4e2f-b6d7-b11b69a32613", 
        "hp_gpu-node1": "a08901c0-50a5-4e2f-b6d7-b11b69a32613", 
        "hp_gpu-node2": "a08901c0-50a5-4e2f-b6d7-b11b69a32613", 
        "hp_gpu-node3": "a08901c0-50a5-4e2f-b6d7-b11b69a32613", 
        "hp_gpu-node4": "a08901c0-50a5-4e2f-b6d7-b11b69a32613", 
        "hp_gpu-node5": "a08901c0-50a5-4e2f-b6d7-b11b69a32613", 
        "hp_gpu-node6": "a08901c0-50a5-4e2f-b6d7-b11b69a32613", 
        "hp_gpu-node7": "a08901c0-50a5-4e2f-b6d7-b11b69a32613", 
        "infra-vm": "00000000-0000-0000-0000-000000000000"
    }

我想根据上面的字典构建一个翻转字典,同时考虑具有重复值的键。 所以我想将第一个字典中的每个值与其在列表中的键相关联,并且旧字典中具有重复值的键将位于相同的列表值中。

这是我想要得到的结果,使用 ansible

 {
        "00000000-0000-0000-0000-000000000000": ["HostedEngine", "infra-vm"], 
        "2d826ed8-1dbe-4b10-93a1-5ac9734462cb": ["cpu-node0", "cpu-node1", "cpu-node2", "cpu-node3"], 
       "a08901c0-50a5-4e2f-b6d7-b11b69a32613": ["fp_gpu-node0", "fp_gpu-node1", "fp_gpu-node10", "fp_gpu-node11", "fp_gpu-node2", "fp_gpu-node3", "fp_gpu-node4", "fp_gpu-node5", "fp_gpu-node6", "fp_gpu-node7", "fp_gpu-node8", "fp_gpu-node9", "hp_gpu-node0", "hp_gpu-node1", "hp_gpu-node2", "hp_gpu-node3", "hp_gpu-node4", "hp_gpu-node5", "hp_gpu-node6", "hp_gpu-node7"]
 }

在 python 中我会写类似的东西:

reverse_templates = {} 


for key, value in vm_template.items(): 
    if value not in reverse_templates: 
        reverse_templates[value] = [key] 
    else: 
        reverse_templates[value].append(key)

这是我在ansible 中尝试过的,但不起作用:

- name: "Associate each template with related VMs list"
  set_fact:
    reverse_templates: "{{reverse_templates|default({})|combine({item.value:{% if item.value not in reverse_teplates %}value[item.key]{% else %}reverse_templates[item.value] + [item.key]{% endif %}})}}"
  loop: "{{lookup('dict', vm_templates)}}"

这是错误:

TASK [将每个模板与相关 VM 列表关联] ************************************* ****************************************************** ****************************************************** ************** 致命的:[本地主机]:失败! => {“msg”:“模板字符串时模板错误:意外'%'。字符串: {{reverse_templates|default({})|combine({item.value:{% if item.value 不在 reverse_teplates %}value[item.key]{% else %}reverse_templates[item.value] + [item.key]{% endif %}})}}"}

使用Ansible 有什么好的方法可以到达那里吗?

【问题讨论】:

  • 你可能只是想制作一个自定义过滤器插件,所以你可以用 Python 编写它。

标签: python dictionary ansible


【解决方案1】:

下面的任务完成了这项工作

    - set_fact:
        reverse_templates: "{{ reverse_templates|default([]) +
                               [{item: vm_templates|
                                       dict2items|
                                       selectattr('value', 'eq', item)|
                                       map(attribute='key')|
                                       list}] }}"
      loop: "{{ vm_templates.values()|unique|list }}"
    - debug:
        var: reverse_templates

  reverse_templates:
  - a08901c0-50a5-4e2f-b6d7-b11b69a32613:
    - fp_gpu-node0
    - fp_gpu-node1
    - fp_gpu-node10
    - fp_gpu-node11
    - fp_gpu-node2
    - fp_gpu-node3
    - fp_gpu-node4
    - fp_gpu-node5
    - fp_gpu-node6
    - fp_gpu-node7
    - fp_gpu-node8
    - fp_gpu-node9
    - hp_gpu-node0
    - hp_gpu-node1
    - hp_gpu-node2
    - hp_gpu-node3
    - hp_gpu-node4
    - hp_gpu-node5
    - hp_gpu-node6
    - hp_gpu-node7
  - 00000000-0000-0000-0000-000000000000:
    - HostedEngine
    - infra-vm
  - 2d826ed8-1dbe-4b10-93a1-5ac9734462cb:
    - cpu-node0
    - cpu-node1
    - cpu-node2
    - cpu-node3

Ansible 代码类似于 Python

for key in set(vm_templates.values()):
    reverse_templates[key] = [k for k,v in vm_templates.items() if v == key]
print(reverse_templates)

json_query

下面的任务产生相同的结果

    - set_fact:
        reverse_templates: "{{ reverse_templates|default([]) +
                               [{item: vm_templates|
                                       dict2items|
                                       json_query(query)}] }}"
      loop: "{{ vm_templates.values()|unique|list }}"
      vars:
        query: "[?value == '{{ item }}'].key"

【讨论】:

  • 谢谢@Vladimir。完全按照上面的方式运行代码表示没有名为'eq' 的测试,从eq 中删除引号会给出以下错误:fatal: [localhost]: FAILED! => {"msg": "the field 'args' has an invalid value ({u'reverse_templates': u\"{{ reverse_templates|default([]) + [{item: vm_templates| dict2items| selectattr('value', eq, item)| map(attribute='key')| list}] }}\"}), and could not be converted to an dict.The error was: key must be a string\n\n.... 请您进行测试并且它在您这边工作正常吗?
  • 是的。它与Ansible 2.9.6python3-jinja2 2.10.1-2 一起工作。见eq。试试“==”,它是等价的。
  • 我想知道您为什么发布 irrelevant syntax error 而不是 relevant missing 'eq'?在发表评论之前,请确保您了解mcve。尤其是本案中关于验证的部分。
  • 对不起,你是对的,这是错误,与 eq==equalto 相同:An exception occurred during task execution. To see the full traceback, use -vvv. The error was: TemplateRuntimeError: no test named '==' fatal: [localhost]: FAILED! => {"msg": "Unexpected failure during module execution.", "stdout": ""}. 这似乎不是 Ansible 问题,而是你的环境有问题,因为我正在使用的盒子有一个较旧的jinja2 版本,这与jinja2 ansible 正在使用的版本相同。
  • 我写了一个小剧本来展示ansible使用的jinja2版本,它输出这个:TASK [jin_ver] ***************************************** ok: [localhost] => {"changed": false, "msg": "2.7.2"}[root@lisa tmp]# rpm -q python-jinja2 python-jinja2-2.7.2-4.el7.noarch [root@lisa tmp]#我不知道是否有一个等效的复古兼容来实现什么我们正在努力。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-31
  • 1970-01-01
相关资源
最近更新 更多