1
2
3
4
5
作者:李晓辉

微信联系:Lxh_Chat

联系邮箱: 939958092@qq.com

收集和处理⽹络信息概述

在使用 Ansible 自动化配置网络信息时,有很多过滤器和查找插件可以帮你收集和处理网络信息。配合从受管主机收集的事实,它们能派上大用场。

比如,标准模块 ansible.builtin.setup,会在每个 Play 的开头自动运行,帮你从每台受管主机搜罗各种与网络相关的数据。而其中的 ansible_facts['interfaces'],实际上是系统里所有网络接口名称的列表。通过这个列表,你可以深入了解系统里每个网络接口的具体信息。

以下是常用的网络相关的facts:

事实名称描述
ansible_facts[‘dns’][‘nameservers’]用于由受管主机(包括在 min 子集中)进行名称解析的 DNS 名称服务器。
ansible_facts[‘domain’]受管主机的域。
ansible_facts[‘all_ipv4_addresses’]受管主机上配置的所有 IPv4 地址。
ansible_facts[‘all_ipv6_addresses’]受管主机上配置的所有 IPv6 地址。
ansible_facts[‘fqdn’]受管主机的完全限定域名(DNS 名称)。
ansible_facts[‘hostname’]非限定主机名,即 FQDN 中第一个句点之前的字符串。

我们来看一个示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
---
- name: 收集并显示网络信息
hosts: all
gather_facts: yes
tasks:
- name: 显示受管主机的 IPv4 地址
debug:
msg: "所有 IPv4 地址: {{ ansible_facts['all_ipv4_addresses'] }}"

- name: 显示受管主机的 IPv6 地址
debug:
msg: "所有 IPv6 地址: {{ ansible_facts['all_ipv6_addresses'] }}"

- name: 显示 DNS 名称服务器
debug:
msg: "DNS 名称服务器: {{ ansible_facts['dns']['nameservers'] }}"

- name: 显示主机名
debug:
msg: "主机名: {{ ansible_facts['hostname'] }}"

ipaddr 过滤器

ansible.utils.ipaddr 是一个非常强大的过滤器,可用于处理 IP 地址相关的任务,比如验证、转换、比较 IP 地址或子网等等。

要使用 ansible.utils.ipaddr 过滤器,必须安装 netaddr Python 模块。在 红帽8 以上,可以通过安装 python3-netaddr 软件包来满足此依赖。

查找 IP 地址

如果值是 IP 地址,则过滤器返回 IP 地址。 如果值不是 IP 地址,则过滤器返回 false

1
2
3
4
5
6
7
8
9
10
11
---
- name: 测试 ansible.utils.ipaddr 过滤器
hosts: localhost
tasks:
- name: 验证一个有效的 IP 地址
debug:
msg: "{{ '192.168.1.1' | ansible.utils.ipaddr }}"

- name: 验证一个无效的 IP 地址
debug:
msg: "{{ 'not_an_ip' | ansible.utils.ipaddr }}"

查找网络掩码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
---
- name: 查找合格的子网掩码
hosts: localhost
gather_facts: no
vars:
# 模拟一堆数据,其中包含有效和无效的子网掩码
netmask_data:
- "255.255.255.0"
- "255.255.0.0"
- "255.0.0.0"
- "invalid_netmask"
- "255.255.128.0"

tasks:
- name: 筛选出有效的子网掩码
debug:
msg: >
合格的子网掩码是:
{{ netmask_data | select('ansible.utils.ipaddr', 'netmask') | list }}

查找主机地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
---
- name: Filter host IPs from a list and display them
hosts: localhost
vars:
listips:
- 192.168.1.1
- 192.168.1.0
- 192.168.1.255
- 10.0.0.1
- 172.16.0.1

tasks:
- name: Filter host IPs from the list
ansible.builtin.set_fact:
host_ips: "{{ listips | ansible.utils.ipaddr('host') }}"

- name: Display the filtered host IPs
ansible.builtin.debug:
msg: "Host IPs: {{ host_ips }}"

查找网络范围

过滤掉不是有效⽹络规格的值,必要时将⼦⽹掩码转换为 CIDR 前缀

1
2
3
4
5
6
7
8
9
10
11
- name: Get Network Information Using ansible.utils.ipaddr
hosts: localhost
gather_facts: no
tasks:
- name: Define IP address and subnet
set_fact:
ip_address: "192.168.1.100/24"

- name: Extract network information
debug:
msg: "The network of {{ ip_address }} is {{ ip_address | ansible.utils.ipaddr('net') }}"

运行结果:

1
2
3
4
TASK [Extract network information] **************
ok: [localhost] => {
"msg": "The network of 192.168.1.100/24 is 192.168.1.0/24"
}

判断CIDR位数

ansible.utils.ipaddr('prefix') 过滤器用于获取 IP 地址的 CIDR 前缀长度(即子网掩码的位数)。例如,192.168.1.100/24 的前缀是 242001:db8::1/64 的前缀是 64

IPv4 和 IPv6 都适用

prefix 提取 CIDR 的子网掩码位数,例如:

  • 192.168.1.100/2424
  • 2001:db8::1/6464
  • 8.8.8.8/3232(单个主机)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- name: Get CIDR prefix length using ansible.utils.ipaddr
hosts: localhost
gather_facts: no
tasks:
- name: Define IP addresses with CIDR
set_fact:
ip_list:
- "192.168.1.100/24"
- "10.0.0.5/16"
- "172.16.30.4/12"
- "2001:db8::1/64"
- "8.8.8.8/32"

- name: Extract prefix length
debug:
msg: "IP: {{ item }}, Prefix Length: {{ item | ansible.utils.ipaddr('prefix') }}"
loop: "{{ ip_list }}"

运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
TASK [Extract prefix length] ************************
ok: [localhost] => (item=192.168.1.100/24) => {
"msg": "IP: 192.168.1.100/24, Prefix Length: 24"
}
ok: [localhost] => (item=10.0.0.5/16) => {
"msg": "IP: 10.0.0.5/16, Prefix Length: 16"
}
ok: [localhost] => (item=172.16.30.4/12) => {
"msg": "IP: 172.16.30.4/12, Prefix Length: 12"
}
ok: [localhost] => (item=2001:db8::1/64) => {
"msg": "IP: 2001:db8::1/64, Prefix Length: 64"
}
ok: [localhost] => (item=8.8.8.8/32) => {
"msg": "IP: 8.8.8.8/32, Prefix Length: 32"
}

判断IP是否是私有IP

ansible.utils.ipaddr('private') 过滤器用于判断一个 IP 地址是否属于 私有 IP 地址(RFC1918 内网地址:10.0.0.0/8、172.16.0.0/12、192.168.0.0/16)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- name: Check if IP address is private
hosts: localhost
gather_facts: no
tasks:
- name: Define IP addresses
set_fact:
ip_list:
- "192.168.1.100"
- "10.0.0.5"
- "172.16.30.4"
- "8.8.8.8"

- name: Check if each IP is private
debug:
msg: "{{ item }} is private: {{ item | ansible.utils.ipaddr('private') }}"
loop: "{{ ip_list }}"

运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
TASK [Check if each IP is private] ***************
ok: [localhost] => (item=192.168.1.100) => {
"msg": "192.168.1.100 is private: True"
}
ok: [localhost] => (item=10.0.0.5) => {
"msg": "10.0.0.5 is private: True"
}
ok: [localhost] => (item=172.16.30.4) => {
"msg": "172.16.30.4 is private: True"
}
ok: [localhost] => (item=8.8.8.8) => {
"msg": "8.8.8.8 is private: False"
}

判断是否为公网IP

ansible.utils.ipaddr('public') 过滤器用于判断一个 IP 地址是否是 公网 IP 地址(即不属于 RFC1918 私有 IP 段)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- name: Check if IP address is public
hosts: localhost
gather_facts: no
tasks:
- name: Define IP addresses
set_fact:
ip_list:
- "192.168.1.100"
- "10.0.0.5"
- "172.16.30.4"
- "8.8.8.8"
- "1.1.1.1"

- name: Check if each IP is public
debug:
msg: "{{ item }} is public: {{ item | ansible.utils.ipaddr('public') }}"
loop: "{{ ip_list }}"

运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
TASK [Check if each IP is public] ***************
ok: [localhost] => (item=192.168.1.100) => {
"msg": "192.168.1.100 is public: False"
}
ok: [localhost] => (item=10.0.0.5) => {
"msg": "10.0.0.5 is public: False"
}
ok: [localhost] => (item=172.16.30.4) => {
"msg": "172.16.30.4 is public: False"
}
ok: [localhost] => (item=8.8.8.8) => {
"msg": "8.8.8.8 is public: True"
}
ok: [localhost] => (item=1.1.1.1) => {
"msg": "1.1.1.1 is public: True"
}

ipwrap 过滤器

  • IPv4 地址ipwrap 不会做任何修改
  • IPv6 地址ipwrap 会添加方括号 [ ],确保在 URL 或某些配置文件(如 nginx.conf)中解析时不会出错。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- name: Wrap IP addresses using ansible.utils.ipwrap
hosts: localhost
gather_facts: no
tasks:
- name: Define IP addresses
set_fact:
ip_list:
- "192.168.1.100"
- "10.0.0.5"
- "172.16.30.4"
- "8.8.8.8"
- "2001:db8::1"

- name: Apply ipwrap filter
debug:
msg: "Original: {{ item }}, Wrapped: {{ item | ansible.utils.ipwrap }}"
loop: "{{ ip_list }}"

运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
TASK [Apply ipwrap filter] ************************
ok: [localhost] => (item=192.168.1.100) => {
"msg": "Original: 192.168.1.100, Wrapped: 192.168.1.100"
}
ok: [localhost] => (item=10.0.0.5) => {
"msg": "Original: 10.0.0.5, Wrapped: 10.0.0.5"
}
ok: [localhost] => (item=172.16.30.4) => {
"msg": "Original: 172.16.30.4, Wrapped: 172.16.30.4"
}
ok: [localhost] => (item=8.8.8.8) => {
"msg": "Original: 8.8.8.8, Wrapped: 8.8.8.8"
}
ok: [localhost] => (item=2001:db8::1) => {
"msg": "Original: 2001:db8::1, Wrapped: [2001:db8::1]"
}

举个例子:

如果你需要生成一个 Nginx 配置,其中服务器监听 IPv6 地址:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- name: Generate Nginx config with IPv6 support
hosts: localhost
gather_facts: no
tasks:
- name: Define server IP
set_fact:
server_ip: "2001:db8::1"

- name: Create Nginx config
copy:
dest: "/tmp/nginx.conf"
content: |
server {
listen 80;
listen [{{ server_ip | ansible.utils.ipwrap }}]:80;
server_name example.com;
}

这样 listen [2001:db8::1]:80; 语句就能正确解析,而不会因为 : 符号导致错误。

格式化⽹络信息输出

获取前几个地址

获取 IP 网络的前 X 个可用 IP 地址

1
2
3
4
5
6
7
8
9
10
11
- name: Get first 5 available IPs from a subnet
hosts: localhost
gather_facts: no
tasks:
- name: Define subnet
set_fact:
subnet: "192.0.2.0/24"

- name: Extract first 5 available IPs
debug:
msg: "{{ subnet | ansible.utils.ipaddr(5) }}"

获取所有可用地址

ansible.utils.ipaddr('range_usable') 过滤器用于获取 子网中的所有可用 IP 地址,即去掉 网络地址和广播地址 后的 IP 地址范围。

1
2
3
4
5
6
7
8
9
10
11
- name: Get all usable IPs in a subnet
hosts: localhost
gather_facts: no
tasks:
- name: Define subnet
set_fact:
subnet: "192.0.2.0/29"

- name: Get usable IP range
debug:
msg: "{{ subnet | ansible.utils.ipaddr('range_usable') }}"

运行结果

1
2
3
4
5
6
7
8
9
10
11
TASK [Get usable IP range] *************************
ok: [localhost] => {
"msg": [
"192.0.2.1",
"192.0.2.2",
"192.0.2.3",
"192.0.2.4",
"192.0.2.5",
"192.0.2.6"
]
}

判断IP是否属于某网段

ansible.utils.network_in_usable 过滤器用于判断 某个 IP 地址是否在指定的子网范围内,并且是可用的主机地址(即 不是网络地址、广播地址或其他保留地址)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- name: Check if an IP is usable in a subnet
hosts: localhost
gather_facts: no
tasks:
- name: Define subnet and IPs
set_fact:
subnet: "192.0.2.0/29"
test_ips:
- "192.0.2.0" # 网络地址(不可用)
- "192.0.2.1" # 可用
- "192.0.2.6" # 可用
- "192.0.2.7" # 广播地址(不可用)

- name: Check if IPs are usable
debug:
msg: "IP {{ item }} usable: {{ subnet | ansible.utils.network_in_usable(item) }}"
loop: "{{ test_ips }}"

运行结果

1
2
3
4
5
6
7
8
9
10
11
12
13
TASK [Check if IPs are usable] *************************
ok: [localhost] => (item=192.0.2.0) => {
"msg": "IP 192.0.2.0 usable: False"
}
ok: [localhost] => (item=192.0.2.1) => {
"msg": "IP 192.0.2.1 usable: True"
}
ok: [localhost] => (item=192.0.2.6) => {
"msg": "IP 192.0.2.6 usable: True"
}
ok: [localhost] => (item=192.0.2.7) => {
"msg": "IP 192.0.2.7 usable: False"
}

获取下一个地址是什么

ansible.utils.ipaddr('next_usable') 过滤器用于 获取下一个可用的主机 IP 地址,即跳过 网络地址、广播地址,并返回 下一个可用 IP

1
2
3
4
5
6
7
8
9
10
11
- name: Get next usable IP
hosts: localhost
gather_facts: no
tasks:
- name: Define an IP with subnet
set_fact:
ip_with_subnet: "192.0.2.5/24"

- name: Get the next usable IP
debug:
msg: "{{ ip_with_subnet | ansible.utils.ipaddr('next_usable') }}"

运行结果

1
2
3
4
TASK [Get the next usable IP] *************************
ok: [localhost] => {
"msg": "192.0.2.6"
}

cidr_merge 过滤器

ansible.utils.cidr_merge 过滤器用于 合并多个 CIDR 子网,将它们优化成更少、更大的子网范围。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- name: Merge multiple CIDR subnets
hosts: localhost
gather_facts: no
tasks:
- name: Define CIDR subnets
set_fact:
cidr_list:
- "192.168.1.0/25"
- "192.168.1.128/25"
- "192.168.2.0/24"
- "192.168.3.0/24"
- "10.0.0.0/8"
- "10.1.0.0/16"

- name: Merge subnets
debug:
msg: "{{ cidr_list | ansible.utils.cidr_merge }}"

运行结果

1
2
3
4
5
6
7
8
9
TASK [Merge subnets] *************************
ok: [localhost] => {
"msg": [
"10.0.0.0/8",
"192.168.1.0/24",
"192.168.2.0/24",
"192.168.3.0/24"
]
}

更多网络过滤器用法,请点击Using the ipaddr filter