我正在寻找一个表达式来获取 Linux 和 FreeBSD 中分配给该 iface 的 ip 地址的接口名称。
这个问题基于这个答案:https://serverfault.com/a/948288/416946
在 Debian 上,此 jinja2 表达式将返回接口对象(来自 ansible facts)given_ip
iface_for_ip: >-
{{ ansible_facts
| dict2items
| selectattr('value.ipv4', 'defined')
| selectattr('value.ipv4.address', 'equalto', given_ip)
| first }}
然而这在 FreeBSD 上不起作用,因为该ipv4
结构是一个数组,而不是一个对象。
如果你只运行这个代码片段:
iface_for_ip: >-
{{ ansible_facts
| dict2items
| selectattr('value.ipv4', 'defined') }}
你将获得如下输出:
在 Debian 上 - key: eth0
value:
active: true
device: eth0
ipv4:
address: 10.8.20.206
broadcast: 10.8.20.255
netmask: 255.255.255.0
network: 10.8.20.0
ipv6:
- address: fe80::84ee:35ff:fed4:a23c
prefix: '64'
scope: link
macaddress: 00:ee:35:00:00:00
mtu: 1500
promisc: false
speed: 10000
type: ether
在 FreeBSD 上
- key: epair0b
value:
device: epair0b
flags:
- UP
- BROADCAST
- RUNNING
- SIMPLEX
- MULTICAST
ipv4:
- address: 10.8.20.207
broadcast: 10.8.20.255
netmask: 255.255.255.0
network: 10.8.20.0
ipv6: []
macaddress: 00:ee:23:00:00:00
media: Ethernet
media_options:
- full-duplex
media_select: 10Gbase-T
media_type: 10Gbase-T
metric: '0'
mtu: '1500'
options:
- PERFORMNUD
status: active
type: ether
如何使用 jinja2 ansible 表达式来获取跨平台仅给出 ip 地址的接口? json_query
在这里可能会有用,但是方法我不清楚。
答案1
收集的数据存在差异设置在 Debian 和 FreeBSD 上。
在 Ubuntu(Debian 衍生产品)中,属性IPv4是一本字典。次要地址存储在列表中ipv4_secondaries。第一步是创建设备和 ipv4 地址列表。例如
- debug:
var: ansible_facts.distribution
- set_fact:
ifc_list: "{{ ansible_facts|
dict2items|
json_query(query)|
selectattr('ipv4')|list }}"
vars:
query: "[?value.type == 'ether'].{device: value.device,
ipv4: value.ipv4.address}"
- debug:
var: ifc_list
给予(节略)
ansible_facts.distribution: Ubuntu
ifc_list:
- device: eth0
ipv4: 10.1.0.27
然后“选择给定 IP 地址的网络接口”
- set_fact:
iface_for_ip: "{{ ifc_list|
selectattr('ipv4', 'eq', ip_address)|
map(attribute='device')|list }}"
vars:
ip_address: "10.1.0.27"
- debug:
var: iface_for_ip
给予(节略)
iface_for_ip:
- eth0
在 FreeBSD 中,属性IPv4是一个列表。创建设备和 ipv4 列表
- debug:
var: ansible_facts.distribution
- set_fact:
ifc_list: "{{ ansible_facts|
dict2items|
json_query(query)|
selectattr('ipv4')|list }}"
vars:
query: "[?value.type == 'ether'].{device: value.device,
ipv4: value.ipv4[].address}"
- debug:
var: ifc_list
给予(节略)
ansible_facts.distribution: FreeBSD
ifc_list:
- device: wlan0
ipv4:
- 10.1.0.51
然后“选择给定 IP 地址的网络接口”
- set_fact:
iface_for_ip: "{{ iface_for_ip|default([]) + [item.device] }}"
loop: "{{ ifc_list }}"
when: ip_address in item.ipv4
vars:
ip_address: "10.1.0.51"
- debug:
var: iface_for_ip
给予(节略)
iface_for_ip:
- wlan0
从 Ansible 2.8 开始,你可以使用测试包含。下面的任务给出了相同的结果
- set_fact:
iface_for_ip: "{{ ifc_list|
selectattr('ipv4', 'contains', ip_address)|
map(attribute='device')|
unique }}"
vars:
ip_address: "10.1.0.51"
问:“如何删除最后的 set_fact + 循环以便它可以纯粹在 vars 文件中定义?“
答:属性IPv4是一个列表。要使用选择属性, 代替环形,你需要一个测试 包含(序列,值)。Ansible 2.7 及以下版本中没有这样的测试。只有输入(值,序列)可以使用参数反转顺序的测试。如果您没有 Ansible 2.8 或更高版本,则必须编写自己的测试。例如
shell> cat test_plugins/contains.py
def contains(l, value):
return value in l
class TestModule:
"""Main test class from Ansible."""
def tests(self):
"""Add these tests to the list of tests available to Ansible."""
return {
'contains': contains
}
然后设置事实给出相同的结果。该表达式也可以用于瓦尔斯
- set_fact:
iface_for_ip: "{{ ifc_list|
selectattr('ipv4', 'contains', ip_address)|
map(attribute='device')|list }}"
答案2
受到其他答案的启发那里链接的要点
vars:
freebsd_query: "[*].{device: device, active: active, ipv6: ipv6, ipv4: ipv4[? address == '{{ ip_find_iface }}']}[?ipv4])" # string must be in ' # sorry, only partial interface info, did not find out how to return all info directly
linux_query: "[?ipv4.address == '{{ ip_find_iface }}']" # string must be in '
ipv6_query: "[*].{device: device, active: active, ipv4: ipv4, ipv6: ipv6[? address == '{{ ip_find_iface }}']}[?ipv6]" # string must be in ' # sorry, only partial info, did not find how to return the full one
ip_query: "{{ ip_find_iface | ipv6 | ternary(
ipv6_query,
ansible_facts[ansible_facts.interfaces | first].ipv4 is mapping | ternary(linux_query, freebsd_query)
) }}.device"
all_interfaces: "{{ ansible_facts.interfaces | map('extract', ansible_facts) }}"
iface_for_ip: "{{ all_interfaces | json_query(ip_query) }}"
ip_query
首先检查 ip 是否为 ipv6 格式。如果不是,则检查是否{anyNetwork}.ipv4
为字典。json_query()
根据此选择查询。
(可能有错别字,因为我无法(简单地)测试所有内容。已经更正了大约 5 次……)
(如何发展:https://gist.github.com/Ramblurr/5d8324e0154ea6be52407618222fcaf7)