Простое решение
Давайте создадим список словарей, которые должны быть удалены
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
give
"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
для упрощения сравнения словарей и поместим его в переменные
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
}