修改和替换列表元素的最佳方法是什么?

时间:2019-07-30 04:00:22

标签: ansible

原始列表具有以下结构:

endpoints:
-   address: 10.10.10.1
    name: hostname1:867
    write: yes
-   address: 10.10.10.2
    name: hostname2:867
    write: yes
-   address: 10.10.10.3
    name: hostname3:867
    write: yes

我正在尝试通过拆分“名称”字段来创建新列表,结果如下:

endpoints:
-   address: 10.10.10.1
    name: hostname1
    port: 867
    write: yes
-   address: 10.10.10.2
    name: hostname2
    port: 867
    write: yes
-   address: 10.10.10.3
    name: hostname3
    port: 867
    write: yes

我尝试了json_query和map('regex_replace')的组合,然后设置了一个新事实,但没有成功。

2 个答案:

答案 0 :(得分:0)

虽然使用json_query似乎是合理的选择,但由于JMESPath(构建json_query的项目)缺少split函数,因此我不确定它的工作方式。

您可以考虑编写自己的过滤器插件。它增加了将来的剧本维护的复杂性,但是以这种方式进行数据操作在python中要容易得多。

在您的剧本目录中,创建一个名为filter_plugins的文件夹和一个包含内容的文件split_hostname.py

#!/usr/bin/env python

class FilterModule(object):
    def filters(self):
        return {'split_hostname': split_hostname}

def split_hostname(endpoints):
    new_endpoints = []
    for endpoint in endpoints:
        hostname, port = endpoint["name"].split(":")
        endpoint["name"] = hostname
        endpoint["port"] = port
        new_endpoints.append(endpoint)
    return new_endpoints

然后在您的剧本中使用过滤器:

    - set_fact:
        new_endpoints: "{{ endpoints | split_hostname }}"

这是一个非常基本的示例。如果您选择沿着这条路走,则可能需要添加一些错误处理。

答案 1 :(得分:0)

以下任务

- set_fact:
    ep2: "{{ ep2|
             default([]) + [
             {'write': item.write,
             'address': item.address,
             'port': item.name.split(':').1,
             'name': item.name.split(':').0} ]
             }}"
  loop: "{{ endpoints }}"
- debug:
    var: ep2

给予

  ep2:
  - address: 10.10.10.1
    name: hostname1
    port: '867'
    write: true
  - address: 10.10.10.2
    name: hostname2
    port: '867'
    write: true
  - address: 10.10.10.3
    name: hostname3
    port: '867'
    write: true

下面的任务更加灵活,仅在定义时添加项目 write 地址

- set_fact:
    ep: "{{ ep|
            default([]) + [ {}|
            combine((item.write is defined)|
                     ternary({'write': item.write}, {}))|
            combine((item.address is defined)|
                     ternary({'address': item.address}, {}))|
            combine({'port': item.name.split(':').1})|
            combine({'name': item.name.split(':').0}) ]
            }}"
  loop: "{{ endpoints }}"

如果可以使用完整的 item ,则可以简化任务

- set_fact:
    ep: "{{ ep|
            default([]) + [
            item|
            combine({'port': item.name.split(':').1})|
            combine({'name': item.name.split(':').0}) ]
            }}"
  loop: "{{ endpoints }}"

如果您想尝试 filter_plugins ,请使用dict_utils。以下是简化的 dict_add_hash 过滤器。

$ cat filter_plugins/dict_utils.py
def dict_add_hash(d, h):
    for k, v in h.iteritems():
        d[k] = v
    return d

class FilterModule(object):
    ''' Ansible filters. Interface to Python dictionary methods.'''

    def filters(self):
        return {
            'dict_add_hash' : dict_add_hash
        }

任务

- set_fact:
    ep2: "{{ ep2|
             default([]) + [
             item|
             dict_add_hash({'port': item.name.split(':').1})|
             dict_add_hash({'name': item.name.split(':').0}) ]
             }}"
  loop: "{{ endpoints }}"
- debug:
    var: ep2

给出相同的结果

  ep2:
  - address: 10.10.10.1
    name: hostname1
    port: '867'
    write: true
  - address: 10.10.10.2
    name: hostname2
    port: '867'
    write: true
  - address: 10.10.10.3
    name: hostname3
    port: '867'
    write: true

查看所有可用的plugins