使用 Ansible Facts 通过 PCI 地址配置网卡
使用 Ansible Facts 通过 PCI 地址配置网卡
在这篇文章中,您将学习 Ansible facts 在配置 Linux 网络方面的高级应用。您将了解如何通过 PCI 地址指定网络设备,而不是硬编码设备名称。这使您的配置能够在具有不同网络命名方案的不同 Red Hat Enterprise Linux 版本上工作。
Red Hat Enterprise Linux 系统角色
RHEL 系统角色 在多个 RHEL 版本之间提供统一的配置接口。但是,现代 Linux 发行版中网络设备的名称在不同的版本中往往不稳定。过去,内核根据设备出现的顺序为其命名。第一个设备名为 eth0
,下一个名为 eth1
,依此类推。
为了使设备名称更可靠,开发人员引入了 其他方法。这干扰了基于接口名称创建与发行版无关的网络配置。解决此问题的初始方案是通过 MAC 地址访问网卡。但这需要一个包含所有网卡 MAC 地址的最新清单。此外,在更换损坏的硬件后,还需要更新清单。这会导致额外的工作。为了避免这种工作,能够通过其 PCI 地址指定网卡将非常棒。在硬件配置统一的情况下(相同的型号、相同的插槽、相同的主板),PCI 地址应该是稳定的。这是因为它定义了 PCI 总线、设备和功能。
Ansible facts
Ansible facts 已经将网卡的 PCI 地址作为 pciid
公开。以下剧本展示了如何获取网卡 enp0s31f6
的 PCI 地址
--- - hosts: localhost vars: nic: enp0s31f6 tasks: - name: Show PCI address (pciid) for a network card debug: msg: "The PCI address for {{ nic }} is {{ ansible_facts[nic]['pciid'] }}."
运行剧本时,它显示在这种情况下 PCI 地址为 0000:00:1f.6
ansible-playbook show_pciid.yml [...] TASK [Show PCI address (pciid) for a network card] ************************** ok: [localhost] => { "msg": "The PCI address for enp0s31f6 is 0000:00:1f.6." } [...]
转换 facts
通过 PCI 地址选择网卡并不总是那么简单。Ansible facts 无法直接通过其属性查询设备。幸运的是,Ansible 中有 过滤器 使重新组织 facts 成为可能。其中, json_query 过滤器允许用户使用 JSON 的 JMESPath 查询语言重新组织和过滤数据。要使用它,您可能需要安装 python2-jmespath
或 python3-jmespath
软件包。Ansible 使用一个字典来组织网络 facts,其中设备名称作为键。但我们需要键为 PCI 地址。为此,我们将使用一个 JMESPath 表达式,该表达式提取 Ansible facts 字典的所有值(@.*
),然后仅选择包含 pciid
键的值([?pciid]
)。然后,我们将使用表达式 {key: pciid, value: device}
创建一个新字典,其中一个名为 key 的项目用于 PCI ID,另一个名为 value 的项目用于接口名称。这种结构允许我们使用 items2dict 过滤器(在 Ansible 2.7 中引入)来构建最终字典。
示例
以下剧本展示了如何通过这种方式创建字典 device_by_pci_address
。它将包含从 PCI 地址到设备名称的映射
--- - hosts: localhost vars: pci_address: "0000:00:1f.6" device_by_pci_address: "{{ ansible_facts | json_query('@.* | [?pciid].{key: pciid, value: device}') | items2dict }}"
以下任务展示了此字典的结构以及如何使用它
tasks: - name: Show devices by PCI address debug: var: device_by_pci_address - name: "Show device with PCI address {{ pci_address }}" debug: msg: "The device {{ device_by_pci_address[pci_address] }} is at the PCI address {{ pci_address }}"
运行这些任务时,Ansible 输出以下内容
TASK [Show devices by PCI address] ***************************************** ok: [localhost] => { "device_by_pci_address": { "0000:00:1f.6": "enp0s31f6", "0000:3a:00.0": "wlp58s0", "6-1:1.0": "enp8s0u1" } } TASK [Show device with PCI address 0000:00:1f.6] *************************** ok: [localhost] => { "msg": "The device enp0s31f6 is at the PCI address 0000:00:1f.6" }
如果您仔细观察,您会注意到一个设备具有不同的 PCI 地址格式(6-1:1.0
)。这实际上是一个 USB 设备。在虚拟机上,您可能会遇到其他类型的地址。Virtio 设备的地址类似于 virtio0
、virtio1
等。在配置中使用设备名称使其仍然特定于某些版本。稍作修改,也可以查找 MAC 地址
--- - hosts: localhost vars: pci_address: "0000:00:1f.6" macaddress_by_pci_address: "{{ ansible_facts | json_query('@.* | [?pciid].{key: pciid, value: macaddress}') | items2dict }}" [...]
请注意,我们在这里将 value: device
更改为 value: macaddress
。
与网络角色结合
为了将所有这些结合起来,以下是如何使用这些变量与 网络 RHEL 系统角色 结合使用的示例
--- - hosts: localhost vars: pciid: "0000:00:1f.6" macaddress_by_pci_address: "{{ ansible_facts | json_query('@.* | [?pciid].{key: pciid, value: macaddress}') | items2dict }}" network_connections: - name: internal_network mac: "{{ macaddress_by_pci_address[pciid] }}" type: ethernet state: up ip: address: - 192.0.2.73/31 tasks: - name: Import network role import_role: name: rhel-system-roles.network
这将配置连接配置文件 internal_network。它使用设备的 MAC 地址将配置文件限制到 PCI 地址为 0000:00:1f.6
的设备。
展望
由于磁盘上的配置仍然使用 MAC 地址,因此更改网卡需要再次运行剧本。为了避免这种情况,NetworkManager 需要允许在配置中直接指定 PCI 地址。我为 NetworkManager 提交了一个 RFE 建议,以在将来支持此功能。根据安装的 Jinja2 模板引擎版本,dict()
构造函数允许在没有 items2dict
的情况下创建字典
vars: macaddress_by_pci_addresss: "{{ dict(ansible_facts | json_query('@.* | [?pciid].[pciid, macaddress]')) }}"
这在 RHEL 8 和最新版本的 Fedora 上有效。但是, RHEL 7 尚未支持它。
结论
在这篇文章中,我们学习了现代 Linux 版本中的网络接口命名。在较大的环境中,识别网卡 PCI 地址的能力对于保持一致性非常有用。能够在 Ansible 自动化中转换 facts 提供了许多可能性,包括使用 facts 来识别在与 RHEL 系统角色或任何其他角色一起使用时要配置哪个设备。
如果您有兴趣了解 Ansible 社区和 Red Hat 批准的认证网络模块,请查看 [nsible 自动化认证内容!或者,您可以详细了解 Ansible 网络自动化解决方案。