【问题标题】:set_facts with dict as argument of a loopset_facts 与 dict 作为循环的参数
【发布时间】:2020-02-17 18:46:51
【问题描述】:

我想像这样获取按master分组的桥接接口列表:

brv100:
 - vnet0
 - eth0
brv101:
  - vnet1
  - eth1

我想使用 shell 命令的原生 json 输出。 我唯一能做的就是获得预定义数量的接口,如下所示:

- hosts: localhost
  gather_facts: no

  tasks:
    - shell:
        cmd: ip -details -pretty -json link show type bridge
      register: list_bridges

    - set_fact:
        bridges: "{{ list_bridges.stdout }}"

    - debug: 
        msg: "{{ bridges | map(attribute='ifname') | list}}"

    - name: get json
      shell:
        cmd: ip -details -pretty -json link show master "{{ifname}}"
      with_items: "{{bridges | map(attribute='ifname') | list}}" 
      loop_control:
        loop_var: ifname
      register: list_interfaces

    - set_fact:
        interfaces: "{{ list_interfaces.results | map(attribute='stdout') | list }}"

    - set_fact:
        toto: "{{interfaces.1}} + {{interfaces.2}}"
    - debug:
        msg: "{{toto | map(attribute='ifname')|list}}"

现在如果我想对循环做同样的事情:

    - set_fact:
    toto: " {{item|default([])}}+ {{ item |default([])}}.{{idx}} "
  loop: "{{interfaces}}"
  loop_control:
   label: "{{item}}"
   index_var: idx
   - debug: var=toto

结果似乎不是列表列表,而是字符串列表,我无法通过简单的调试提取“ifname”值

    - debug:
        msg: "{{toto | map(attribute='ifname')|list}}"

我应该怎么做才能从 json 原生输出中受益并获得桥接接口的简单列表(就像以前使用的 brctl show 一样)?

【问题讨论】:

    标签: list loops ansible


    【解决方案1】:

    ansible_facts 中提供了按主设备分组的桥接接口列表。下面的任务设置桥接和桥接接口的字典

    - set_fact:
        bridges: "{{ dict(ansible_facts|
                     dict2items|
                     json_query('[?value.type == `bridge`].[key, value.interfaces]')) }}"
    

    问:“设法通过处理 JSON 数据获得相同的结果。”

    答:ip -json ... 命令的输出是 JSON 格式的字符串,必须通过 from_yaml 过滤器将其转换为 Ansible 中的 JSON 字典(JSON 是 YAML 的子集)。例如,下面的任务给出相同的结果

      vars:
    
        my_debug: false
    
      tasks:
    
        - name: Get bridges names
          command: "ip -details -json link show type bridge"
          register: list_bridges
        - set_fact:
            bridges: "{{ list_bridges.stdout|
                         from_yaml|
                         map(attribute='ifname')|
                         list }}"
        - debug:
            var: bridges
          when: my_debug
    
        - name: Get bridges interfaces
          command: "ip -details -json link show master {{ item }}"
          loop: "{{ bridges }}" 
          register: list_interfaces
        - set_fact:
            bridges_interfaces: "{{ list_interfaces.results|
                                    json_query('[].stdout')|
                                    map('from_yaml')|
                                    list }}"
    
        - debug:
            msg: "{{ msg.split('\n') }}"
          vars:
            msg: "{{ item|to_nice_yaml }}"
          loop: "{{ bridges_interfaces }}"
          loop_control:
            label: "{{ item|json_query('[].ifname') }}"
          when: my_debug
    
        - name: Set dictionary of bridges
          set_fact:
            bridges_dict: "{{ bridges_dict|
                              default({})|
                              combine({item.0: item.1|json_query('[].ifname')}) }}"
          loop: "{{ bridges|zip(bridges_interfaces)|list }}"
          loop_control:
            label: "{{ item.1|json_query('[].ifname') }}"
        - debug:
            var: bridges_dict
    


    将桥写入文件的模板
    {% for k,v in bridges_dict.items() %}
    {{ k }}:
    {% if v is iterable %}
    {% for i in v %}
      - {{ i }}
    {% endfor %}
    {% endif %}
    {% endfor %}
    
        - name: Write the bridges to file
          template:
            src: bridges.txt.j2
            dest: bridges.txt
    

    文件bridges.txt将在运行任务的远程主机中创建。

    【讨论】:

    • 谢谢,解决方法很简单,使用 ansible_facts 格式的 json。但是如何处理不同的 json 输入,比如 ip 命令的 shell 输出?无论如何,我无法通过处理以“[{”开头的 json 数据获得相同的结果。我确信使用 json 输入是一个优势,但似乎需要进行大量转换才能获得想要的显示结果。我很高兴知道这样的尝试是否可行。
    • json data that begin with "[{" 是一个字符串,而不是 JSON。这就是麻烦的根源。命令 ip -details -json ... 返回 JSON 格式的字符串。将其转换为 YAML。例如list_bridges.stdout|from_yaml.
    • 非常感兴趣的方式,我从你的代码以及如何使用json_query中学到了很多东西。现在我尝试通过 jinja2 模板获得相同的结果:{% for bridge in bridges %} - {{ bridge }}: {% for interface in interfaces %} {% if interface|json_query('[].master') == bridge %} {{ interface|json_query('[].ifname') }} {% endif %} {% endfor %} {% endfor %} 没有任何成功...你有什么想法吗?
    • json_query 的结果是一个列表。该条件无法将列表与字符串进行比较。使用{% if interface|json_query('[].master')|first == bridge %}。但是,我宁愿为此使用bridges_dict
    • 我在答案中添加了一个模板示例。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-27
    • 2018-06-20
    • 1970-01-01
    • 2019-03-09
    • 1970-01-01
    相关资源
    最近更新 更多