如何使用Ansible获取物理网卡列表
如何通过Ansible获取物理NIC的列表
你想做什么?
过去,当我们针对 eth.* 进行配置时,它适用于所有物理网络接口的名称。然而,在如今纷乱的时代中,出现了 Consistent Network Device Naming(CNDN)和 Predictable Network Interface Names(PNIN)等方式,因此使用正则表达式来匹配网络接口名称可能会有些不保险(当然,如果禁用CNDN和PNIN,就可以回到过去)。因此,我们将尝试使用Ansible以现代的方式创建物理网络接口名称列表。
首先,需要预先创建ansible.cfg和清单。
[defaults]
inventory = inventory
localhost ansible_connection=local
查看ansible_interfaces
[root@localhost ~]# ansible all -m setup -a 'filter=ansible_interfaces'
localhost | success >> {
"ansible_facts": {
"ansible_interfaces": [
"lo",
"docker0",
"eth1",
"eth0"
]
},
"changed": false
}
暫時總結,已經獲取了所有網路介面的名稱,但是 lo 和 docker0 有點礙事。我們是否有辦法提取只有物理網路介面的線索,並在設置模塊中檢視可以得到的全部facts。
在setup模块中可以获取的事实
[root@localhost ~]# ansible all -m setup
localhost | success >> {
"ansible_facts": {
...
"ansible_docker0": {
"active": false,
"device": "docker0",
"id": "8000.56847afe9799",
"interfaces": [],
"ipv4": {
"address": "192.168.123.1",
"netmask": "255.255.255.0",
"network": "192.168.123.0"
},
"macaddress": "56:84:7a:fe:97:99",
"mtu": 1500,
"promisc": false,
"stp": false,
"type": "bridge"
},
...
"ansible_eth0": {
"active": true,
"device": "eth0",
"ipv4": {
"address": "10.0.2.15",
"netmask": "255.255.255.0",
"network": "10.0.2.0"
},
"ipv6": [
{
"address": "fe80::a00:27ff:fe96:8299",
"prefix": "64",
"scope": "link"
}
],
"macaddress": "08:00:27:96:82:99",
"module": "virtio_net",
"mtu": 1500,
"promisc": false,
"type": "ether"
},
"ansible_eth1": {
"active": true,
"device": "eth1",
"ipv4": {
"address": "192.168.56.2",
"netmask": "255.255.255.0",
"network": "192.168.56.0"
},
"ipv6": [
{
"address": "fe80::a00:27ff:fe66:2832",
"prefix": "64",
"scope": "link"
}
],
"macaddress": "08:00:27:66:28:32",
"module": "virtio_net",
"mtu": 1500,
"promisc": false,
"type": "ether"
},
...
"ansible_interfaces": [
"lo",
"docker0",
"eth1",
"eth0"
],
...
"ansible_lo": {
"active": true,
"device": "lo",
"ipv4": {
"address": "127.0.0.1",
"netmask": "255.0.0.0",
"network": "127.0.0.0"
},
"ipv6": [
{
"address": "::1",
"prefix": "128",
"scope": "host"
}
],
"mtu": 65536,
"promisc": false,
"type": "loopback"
},
...
},
"changed": false
}
最好的选择似乎是存在 ansible_{{ 网络接口名称 }}.module 的选项。
在上面的例子中,ansible_{{ 网络接口名称 }}.type 为ether的也可能是物理接口,但是也存在反例。
[root@localhost ~]# ip link add name veth_left type veth peer name veth_right
[root@localhost ~]# ansible all -m setup -a 'filter=ansible_veth_left'
localhost | success >> {
"ansible_facts": {
"ansible_veth_left": {
"active": false,
"device": "veth_left",
"macaddress": "aa:19:3b:6d:4f:b6",
"mtu": 1500,
"promisc": false,
"type": "ether"
}
},
"changed": false
}
[root@localhost ~]# ip link delete dev veth_left
决定将存在 ansible_{{ ネットワークインタフェース名 }}.module 的网络接口名列入列表。
设置事实和使用项
让我们尝试使用 with_items 迭代 ansible_interfaces,同时检查 ansible_{{ 网络接口名称 }}.module 是否存在,然后使用 set_fact 进行设定。
---
- hosts: localhost
tasks:
- set_fact:
pif_name: "{{ item }}"
when: "ansible_{{ item }}.module is defined"
with_items: ansible_interfaces
- debug: var=pif_name
[root@localhost ~]# ansible-playbook foo.yaml
PLAY [localhost] **************************************************************
GATHERING FACTS ***************************************************************
ok: [localhost]
TASK: [set_fact ] *************************************************************
skipping: [localhost] => (item=lo)
skipping: [localhost] => (item=docker0)
ok: [localhost] => (item=eth1)
ok: [localhost] => (item=eth0)
TASK: [debug var=pif_name] ****************************************************
ok: [localhost] => {
"var": {
"pif_name": "eth0"
}
}
PLAY RECAP ********************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0
设置事实是,根据条件“when”跳过“lo”和“docker0”。但是,如果使用“with_items”进行循环,设置事实不会自动创建列表,所以“pif_name”的内容并不如预期。
查看 set_fact 的结果
让我们使用 register 将任务结果设置为一个事实来查看。
---
- hosts: localhost
tasks:
- set_fact:
pif_name: "{{ item }}"
register: set_fact_result
when: "ansible_{{ item }}.module is defined"
with_items: ansible_interfaces
- debug: var=set_fact_result
[root@localhost ~]# ansible-playbook foo.yaml
PLAY [localhost] **************************************************************
GATHERING FACTS ***************************************************************
ok: [localhost]
TASK: [set_fact ] *************************************************************
skipping: [localhost] => (item=lo)
skipping: [localhost] => (item=docker0)
ok: [localhost] => (item=eth1)
ok: [localhost] => (item=eth0)
TASK: [debug var=set_fact_result] *********************************************
ok: [localhost] => {
"var": {
"set_fact_result": {
"changed": false,
"msg": "All items completed",
"results": [
{
"changed": false,
"skipped": true
},
{
"changed": false,
"skipped": true
},
{
"ansible_facts": {
"pif_name": "eth1"
},
"invocation": {
"module_args": "",
"module_name": "set_fact"
},
"item": "eth1"
},
{
"ansible_facts": {
"pif_name": "eth0"
},
"invocation": {
"module_args": "",
"module_name": "set_fact"
},
"item": "eth0"
}
]
}
}
}
PLAY RECAP ********************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0
虽然有很多多余的东西,但如果能够处理注册的结果,似乎可以实现。
从set_fact的结果中提取所需的项目
首先,根据先前的任务,只提取与”when”相符的内容。作为方针。
-
- 跳过被未定义的事物。
-
- 跳过被未定义或定义为false的事物。
- 定义了ansible_facts的事物。
可能有三种选项,但是第一种可能在将来被跳过的任务将会带有”skipped”为false的规范,所以跳过;第二种是确定的,但是与第三种相比条件语句的编写可能比较麻烦,所以选择第三种。
---
- hosts: localhost
tasks:
- set_fact:
pif_name: "{{ item }}"
register: set_fact_result
when: "ansible_{{ item }}.module is defined"
with_items: ansible_interfaces
- set_fact:
pif_names: "{{ set_fact_result.results|selectattr('ansible_facts', 'defined') }}"
- debug: var=pif_names
[root@localhost ~]# ansible-playbook foo.yaml
PLAY [localhost] **************************************************************
GATHERING FACTS ***************************************************************
ok: [localhost]
TASK: [set_fact ] *************************************************************
skipping: [localhost] => (item=lo)
skipping: [localhost] => (item=docker0)
ok: [localhost] => (item=eth1)
ok: [localhost] => (item=eth0)
TASK: [set_fact ] *************************************************************
ok: [localhost]
TASK: [debug var=pif_names] ***************************************************
ok: [localhost] => {
"var": {
"pif_names": "<generator object _select_or_reject at 0x22995a0>"
}
}
PLAY RECAP ********************************************************************
localhost : ok=4 changed=0 unreachable=0 failed=0
鉴于生成器返回了,那么我们把它转为列表吧。
顺带一提,由于句子变长了,我们来换行一下吧。
---
- hosts: localhost
tasks:
- set_fact:
pif_name: "{{ item }}"
register: set_fact_result
when: "ansible_{{ item }}.module is defined"
with_items: ansible_interfaces
- set_fact:
pif_names: >-
{{ set_fact_result.results
| selectattr('ansible_facts', 'defined')
| list }}
- debug: var=pif_names
[root@localhost ~]# ansible-playbook foo.yaml
PLAY [localhost] **************************************************************
GATHERING FACTS ***************************************************************
ok: [localhost]
TASK: [set_fact ] *************************************************************
skipping: [localhost] => (item=lo)
skipping: [localhost] => (item=docker0)
ok: [localhost] => (item=eth1)
ok: [localhost] => (item=eth0)
TASK: [set_fact ] *************************************************************
ok: [localhost]
TASK: [debug var=pif_names] ***************************************************
ok: [localhost] => {
"var": {
"pif_names": [
{
"ansible_facts": {
"pif_name": "eth1"
},
"invocation": {
"module_args": "",
"module_name": "set_fact"
},
"item": "eth1"
},
{
"ansible_facts": {
"pif_name": "eth0"
},
"invocation": {
"module_args": "",
"module_name": "set_fact"
},
"item": "eth0"
}
]
}
}
PLAY RECAP ********************************************************************
localhost : ok=4 changed=0 unreachable=0 failed=0
已经变得相当像了。剩下的工作就是从 pif_names 的每个字典中提取 item 或 ansible_facts.pif_name,将它们放入一个列表中应该就可以了。
---
- hosts: localhost
tasks:
- set_fact:
pif_name: "{{ item }}"
register: set_fact_result
when: "ansible_{{ item }}.module is defined"
with_items: ansible_interfaces
- set_fact:
pif_names: >-
{{ set_fact_result.results
| selectattr('ansible_facts', 'defined')
| map(attribute='ansible_facts.pif_name')
| list }}
- debug: var=pif_names
[root@localhost ~]# ansible-playbook foo.yaml
PLAY [localhost] **************************************************************
GATHERING FACTS ***************************************************************
ok: [localhost]
TASK: [set_fact ] *************************************************************
skipping: [localhost] => (item=lo)
skipping: [localhost] => (item=docker0)
ok: [localhost] => (item=eth1)
ok: [localhost] => (item=eth0)
TASK: [set_fact ] *************************************************************
ok: [localhost]
TASK: [debug var=pif_names] ***************************************************
ok: [localhost] => {
"var": {
"pif_names": [
"eth1",
"eth0"
]
}
}
PLAY RECAP ********************************************************************
localhost : ok=4 changed=0 unreachable=0 failed=0
这样生成了一个列表,但是根据情况可能不会成功。
注意事项:Ansible的facts的键名
[root@localhost ~]# ifdown eth0
[root@localhost ~]# ip link set dev eth0 name bar-eth0
[root@localhost ~]# ansible-playbook foo.yaml
PLAY [localhost] **************************************************************
GATHERING FACTS ***************************************************************
ok: [localhost]
TASK: [set_fact ] *************************************************************
skipping: [localhost] => (item=lo)
skipping: [localhost] => (item=bar-eth0)
ok: [localhost] => (item=eth1)
skipping: [localhost] => (item=docker0)
TASK: [set_fact ] *************************************************************
ok: [localhost]
TASK: [debug var=pif_names] ***************************************************
ok: [localhost] => {
"var": {
"pif_names": [
"eth1"
]
}
}
PLAY RECAP ********************************************************************
localhost : ok=4 changed=0 unreachable=0 failed=0
bar-eth0没有出现。这是因为Ansible替换了fact的键,导致第一个set_fact的when条件不再匹配。让我们将要搜索的键替换为其他内容。
---
- hosts: localhost
tasks:
- set_fact:
pif_name: "{{ item }}"
register: set_fact_result
when: "ansible_{{ item|replace('-', '_') }}.module is defined"
with_items: ansible_interfaces
- set_fact:
pif_names: >-
{{ set_fact_result.results
| selectattr('ansible_facts', 'defined')
| map(attribute='ansible_facts.pif_name')
| list }}
- debug: var=pif_names
[root@localhost ~]# ansible-playbook foo.yaml
PLAY [localhost] **************************************************************
GATHERING FACTS ***************************************************************
ok: [localhost]
TASK: [set_fact ] *************************************************************
skipping: [localhost] => (item=lo)
ok: [localhost] => (item=bar-eth0)
ok: [localhost] => (item=eth1)
skipping: [localhost] => (item=docker0)
TASK: [set_fact ] *************************************************************
ok: [localhost]
TASK: [debug var=pif_names] ***************************************************
ok: [localhost] => {
"var": {
"pif_names": [
"bar-eth0",
"eth1"
]
}
}
PLAY RECAP ********************************************************************
localhost : ok=4 changed=0 unreachable=0 failed=0
在这里,bar-eth0 出现了。
请您提供以下信息作为参考。
请您提供以下内容作为参考。
这个逻辑是 Stack Overflow 的山寨品。