根据一些过滤器从字典中选取3个元素



我有一个字典列表:

"my_dict": [
{"disk": "disk1", "size": 600, "controller": "sc1"},
{"disk": "disk2", "size": 700, "controller": "sc2"},
{"disk": "disk3", "size": 600, "controller": "sc1"},
{"disk": "disk4", "size": 500, "controller": "sc3"},
...
...
]

这个列表中有很多元素,我需要从中挑选3个来制作一个带有备用磁盘的raid1磁盘。

选择3个磁盘的限制条件:

  • 磁盘大小应该最小
  • 磁盘大小应该相等
  • 控制器应相同

如何筛选元素并选择具有上述约束的3个磁盘?我已经用sort((过滤器根据磁盘的大小和控制器对它们进行了排序。但这并不能保证前3个磁盘的大小和控制器相同。

谢谢。

这不是最可读的解决方案,但它很有效:

---
- hosts: localhost
gather_facts: false
vars:
my_dict:
- {"disk": "disk1", "size": 600, "controller": "sc1"}
- {"disk": "disk2", "size": 700, "controller": "sc2"}
- {"disk": "disk3", "size": 600, "controller": "sc1"}
- {"disk": "disk4", "size": 500, "controller": "sc3"}
- {"disk": "disk5", "size": 600, "controller": "sc1"}
- {"disk": "disk6", "size": 700, "controller": "sc1"}
tasks:
- debug:
msg: "{{ (my_dict_candidates[0] | default([]))[0:3] | default([]) }}"
vars:
my_dict_candidates: "{{ my_dict_grouped | map('length') | zip(my_dict_grouped) | selectattr('0', 'gt', 2) | map(attribute='1') }}"
my_dict_grouped: "{{ my_dict | groupby('controller') | map(attribute='1') | map('groupby', 'size') | flatten(levels=1) | map(attribute='1') }}"
TASK [debug] *******************************************************************
ok: [localhost] => {
"msg": [
{
"controller": "sc1",
"disk": "disk1",
"size": 600
},
{
"controller": "sc1",
"disk": "disk3",
"size": 600
},
{
"controller": "sc1",
"disk": "disk5",
"size": 600
}
]
}

这是一个非常复杂的逻辑,您最好编写一个实现它的自定义过滤器,而不是尝试使用纯Jinja,但Jinja解决方案是可行的。

my_dict_grouped中,我们基本上要做两次groupby() | map(attribute='1');映射将从CCD_ 3返回的元组变成更简单的列表。这个步骤可以通过一个支持同时按多个事物分组的过滤器来简化,但我不知道其中一个。

my_dict_candidates中,我们计算出my_dict_grouped中每个列表的长度,然后只选择满足您长度要求的列表。请注意,这会导致一个列表,该列表可能包含0到多个结果,具体取决于输入数据。这就是为什么我们必须在最终表达式中使用空列表的默认值,在该表达式中,我们只保留第一个顶部列表(即最小的可用磁盘(,并只返回前三个结果用于raid阵列。

作为我在评论中的建议的一个例子,这里有一个自定义过滤器的例子,它比您的要求稍远。它将允许您选择具有最小磁盘大小的n磁盘(默认为3((默认为0,即可用于前一数量磁盘的最小磁盘(。请注意,如果没有符合条件的磁盘,筛选器将返回一个空列表。

您可以阅读自定义过滤器的文档,如果需要与团队共享,您将在其中了解如何开发和分发它们的更多信息。对于这个例子,我使用了最简单的方法,并在测试剧本旁边的filter_plugins/目录中添加了我的过滤器,该目录给出了以下目录结构

$ tree
.
├── filter_plugins
│   └── raid_disk_filters.py
└── playbook.yml

这是filter_plugins/raid_disk_filters.py文件。它定义了一个称为smallest_disks_by_controller的单个过滤器

from itertools import groupby

def smallest_disks_by_controller(disk_data, array_size=3, min_disk_size=0):
"""
Returns a list of disk datas for the smallest disks on the same controller
:arg disk_data: the list of dicts containing the disk data. each element
is expected to contain 3 keys: disk, size and controller)
:arg array_size the number of similar disks to return in the result
:arg min_disk_size minimum size of the disk to select, smallest one by default
"""
candidates = []
keyfunc = lambda x: (x['size'], x['controller'])
for key, group in groupby(sorted(disk_data, key=keyfunc), keyfunc):
current_size = key[0]
current_disks = list(group)
if current_size >= min_disk_size and len(current_disks) >= array_size:
candidates = current_disks[:array_size]
break
return candidates

class FilterModule(object):
"""Filters to work with disks for raid array creation"""
def filters(self):
"""Return the filter list."""
return {
'smallest_disks_by_controller': smallest_disks_by_controller
}

这是测试playbook.yml

---
- name: Custom filter demo
hosts: localhost
gather_facts: false
vars:
"my_disks": [{ "disk": "disk1", "size": 900, "controller": "sc1" },{ "disk": "disk2", "size": 700, "controller": "sc2" },{ "disk": "disk3", "size": 600, "controller": "sc1" },{ "disk": "disk4", "size": 500, "controller": "sc3" },{ "disk": "disk5", "size": 600, "controller": "sc1" },{ "disk": "disk6", "size": 700, "controller": "sc2" },{ "disk": "disk7", "size": 600, "controller": "sc1" },{ "disk": "disk8", "size": 500, "controller": "sc3" },{ "disk": "disk9", "size": 600, "controller": "sc1" },{ "disk": "disk10", "size": 700, "controller": "sc2" },{ "disk": "disk11", "size": 600, "controller": "sc1" },{ "disk": "disk12", "size": 500, "controller": "sc3" },{ "disk": "disk13", "size": 600, "controller": "sc1" },{ "disk": "disk14", "size": 700, "controller": "sc2" },{ "disk": "disk15", "size": 600, "controller": "sc1" },{ "disk": "disk16", "size": 500, "controller": "sc3" }]
tasks:
- name: Get a list of matching disks
vars:
msg: |-
3 disks, min size: {{ my_disks | smallest_disks_by_controller }}
3 disks, size >= 700: {{ my_disks | smallest_disks_by_controller(3, 700) }}
4 disks, min size: {{ my_disks | smallest_disks_by_controller(4) }}
4 disks, size >=600: {{ my_disks | smallest_disks_by_controller(4, 600) }}
7 disks, size >=1000: {{ my_disks | smallest_disks_by_controller(7, 1000) }}
debug:
msg: "{{ msg.split('n') }}"

哪个给出:

$ ansible-playbook playbook.yml 
PLAY [Custom filter demo] ***************************************************
TASK [Get a list of matching disks] *****************************************
ok: [localhost] => {
"msg": [
"3 disks, min size: [{'disk': 'disk4', 'size': 500, 'controller': 'sc3'}, {'disk': 'disk8', 'size': 500, 'controller': 'sc3'}, {'disk': 'disk12', 'size': 500, 'controller': 'sc3'}]",
"3 disks, size >= 700: [{'disk': 'disk2', 'size': 700, 'controller': 'sc2'}, {'disk': 'disk6', 'size': 700, 'controller': 'sc2'}, {'disk': 'disk10', 'size': 700, 'controller': 'sc2'}]",
"4 disks, min size: [{'disk': 'disk4', 'size': 500, 'controller': 'sc3'}, {'disk': 'disk8', 'size': 500, 'controller': 'sc3'}, {'disk': 'disk12', 'size': 500, 'controller': 'sc3'}, {'disk': 'disk16', 'size': 500, 'controller': 'sc3'}]",
"4 disks, size >=600: [{'disk': 'disk3', 'size': 600, 'controller': 'sc1'}, {'disk': 'disk5', 'size': 600, 'controller': 'sc1'}, {'disk': 'disk7', 'size': 600, 'controller': 'sc1'}, {'disk': 'disk9', 'size': 600, 'controller': 'sc1'}]",
"7 disks, size >=1000: []"
]
}
PLAY RECAP ******************************************************************
localhost: ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

最新更新