首页 > 解决方案 > Ansible:从字典中删除一个项目

问题描述

我有一个包含键值的字典,很少有值是列表。我想从字典内的列表中删除一个项目。字典看起来像下面有嵌套的项目。

字典看起来像:

title: Some Title
metadata : 
   manifest-version: 1.0
   key: value
installers:
   - name: someName1
     version: 1.0
   - name: someName2
     version: 2.0
   - name: someName3
     packages:
       - fileName: fileName1
         version: 1.1.1
       - fileName: fileName2
         version: 2.2.2
   - name: service
     type: install
     packages:
       - name: serviceName1
         manifest: someManifest2
         source: abcd.tgz
         files:
           - file1.tar
           - file2.tar
       - name: serviceName2
         manifest: someManifest2
         source: efgh.tgz
         files:
           - file3.tar
           - file4.tar
       - name: serviceName3
         manifest: someManifest3
         source: ijkl.tgz
         files:
           - file5.tar
           - file6.tar

如何删除具有的项目name: serviceName3?或者我可以在没有以下项目的情况下将字典复制到另一个字典吗?

最终的字典不应包含以下项目:

- name: serviceName2
  manifest: someManifest2
  source: efgh.tgz
  files:
    - file3.tar
    - file4.tar

标签: pythondictionaryansibleansible-2.xansible-facts

解决方案


简单的解决方案

让我们创建一个应删除的字典列表

  vars:
    ritem:
      - name: serviceName2
        manifest: someManifest2
        source: efgh.tgz
        files:
          - file3.tar
          - file4.tar

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

    - set_fact:
        inst2: "{{ installers|selectattr('packages', 'defined')|list }}"
    - set_fact:
        inst1: "{{ installers|difference(inst2) }}"
    - set_fact:
        inst4: "{{ inst4|default(inst1) + [
                   item|combine({'packages': item.packages|difference(ritem)})] }}"
      loop: "{{ inst2 }}"
    - debug:
        var: inst4

    "inst4": [
        {
            "name": "someName1", 
            "version": 1.0
        }, 
        {
            "name": "someName2", 
            "version": 2.0
        }, 
        {
            "name": "someName3", 
            "packages": [
                {
                    "fileName": "fileName1", 
                    "version": "1.1.1"
                }, 
                {
                    "fileName": "fileName2", 
                    "version": "2.2.2"
                }
            ]
        }, 
        {
            "name": "service", 
            "packages": [
                {
                    "files": [
                        "file1.tar", 
                        "file2.tar"
                    ], 
                    "manifest": "someManifest2", 
                    "name": "serviceName1", 
                    "source": "abcd.tgz"
                }, 
                {
                    "files": [
                        "file5.tar", 
                        "file6.tar"
                    ], 
                    "manifest": "someManifest3", 
                    "name": "serviceName3", 
                    "source": "ijkl.tgz"
                }
            ], 
            "type": "install"
        }
    ]

字典的格式

如果字典没有以完全相同的方式格式化(排序),那么简单的解决方案将不起作用。例如,如果文件的顺序不同,则不会删除该项目

  vars:
    ritem:
      - name: serviceName2
        manifest: someManifest2
        source: efgh.tgz
        files:
          - file4.tar
          - file3.tar

这可以通过几个自定义过滤器来解决。让我们创建列表rhash以简化字典的比较并将其放入 vars

rhash: "{{ ritem|map('dict_flatten')|map('hash')|list }}"

下面的任务给出了相同的结果

    - set_fact:
        inst3: "{{ inst3|default([]) + [
                   filter|
                   list_select_list_bool(item.packages, negative=True)|
                   list] }}"
      vars:
        filter: "{{ item.packages|
                    map('dict_flatten')|
                    map('hash')|
                    map('bool_in', rhash)|
                    list }}"
      loop: "{{ inst2 }}"
    - set_fact:
      inst4: "{{ inst4|default(inst1) + [
                 item.0|combine({'packages': item.1})] }}"
      loop: "{{ inst2|zip(inst3)|list }}"

    - debug:
        var: inst4

自定义过滤器

$ cat filter_plugins/filers.py
def bool_in(x, l):
    return (x in l)

def dict_flatten(d, separator='.'):
    out = {}
    def flatten(x, name=''):
        if type(x) is dict:
            for a in x:
                flatten(x[a], name + a + separator)
        elif type(x) is list:
            i = 0
            for a in sorted(x):
                flatten(a, name + str(i) + separator)
                i += 1
        else:
            out[name[:-1]] = x
    flatten(d)
    return out

def list_select_list_bool(b, l, negative=False):
    l2=[]
    for bi,li in zip(b,l):
        if negative:
            if not bi:
                l2.append(li)
        else:
            if bi:
                l2.append(li)
    return l2

class FilterModule(object):

    def filters(self):
        return {
            'bool_in': bool_in,
            'dict_flatten': dict_flatten,
            'list_select_list_bool': list_select_list_bool
            }

推荐阅读