我们希望听到您的意见!帮助我们深入了解 Ansible 生态系统的现状。
参与 Ansible 项目 2024 年调查

Ansible 和 ServiceNow 第二部分

使用 PyATS/Genie 解析网络设备的事实

这篇博客是关于 Red Hat Ansible Automation 如何与工单自动化集成的一系列文章中的第二部分。这次我们将介绍如何动态地将交换机和路由器的一组网络事实添加到 ServiceNow 工单中。

假设某个网络操作系统软件版本存在一个已知问题,该问题始终会导致问题并影响您的正常运行时间 SLA。您如何说服管理层为升级项目提供资金?您如何向他们证明修复的成本是值得的?更重要的是,您甚至怎么知道?

一个良好的开端是拥有您可以跟踪的指标。能够对工单进行数据挖掘将证明有多少工单涉及运行该有问题的软件版本的硬件。在本博文中,我将向您展示如何自动化将一组事实添加到您未来所有的工单中。然后可以从设备中直接提取无可争议的事实,而无需担心出错或意外遗漏和未创建。

这篇博文将演示如何结合使用 Ansible、Cisco pyATS 和 Cisco Genie 以 JSON 格式返回结构化数据。这使我们能够检索操作显示命令的输出并将其转换为我们想要的任何格式,在本例中将其推送到 ServiceNow 中。

有很多方法可以使用 Ansible 解析网络设备的事实。以下博客示例也可以通过开源的Network Engine Ansible 角色来完成,但在此示例中,我们使用 Cisco 赞助的 pyATS/Genie 实现来解析以下 show version 命令。如您所见,这对于以编程方式交互来说并不十分友好

image7

步骤 1:在 Red Hat Ansible Tower 中创建 Python3 虚拟环境

随着 Ansible Tower 3.5 的发布,我们现在可以使用 Python 3 虚拟环境 (virtualenv) 来增强 playbook 的灵活性和跨 Python 版本的兼容性。这是一个好消息,因为使用 pyATS 和 Genie 包需要 Python3。我们需要创建一个运行 Python3 的新 (virtualenv) 并安装所有依赖项。

su -
yum -y install rh-python36
yum -y install python36-devel gcc
scl enable rh-python36 bash
python3.6 -m venv /var/lib/awx/venv/pyats-sandbox
source /var/lib/awx/venv/pyats-sandbox/bin/activate
umask 0022
pip install pyats genie python-memcached psutil pysnow paramiko
pip install -U "ansible == 2.8

创建自定义 virtualenv 后,Ansible Tower 的作业模板部分会出现一个新字段。您可以从以下下拉菜单中选择您新创建的 venv:image1-6

Cisco 发布了两个对网络自动化非常有用的 Python3 包 - pyATS 和 Genie。第一个,pyATS,充当 Python 框架,而 Genie 则在其基础上构建。Genie 可用于解析、学习和差异化。Genie 的实现是通过在我们的 playbook 中名为 parse_genie 的角色中安装和调用 Galaxy 角色来完成的。

步骤 2:在您的 roles 目录中创建 requirements.yml 文件

roles/requirements.yml

---
- name: parse_genie
  src: https://github.com/clay584/parse_genie
  scm: git
  version: master

默认情况下,Ansible Tower 具有一个系统范围的设置,允许通过 Git 存储库中 requirements.yml 文件动态下载角色。因此,无需像在 CLI 上使用 Ansible Engine 时那样运行ansible-galaxy install -r roles/requirements.yml命令。

有关 Ansible Tower 中项目的更多信息,请参考文档

步骤 3:调用 parse_genie Ansible 角色

现在您已经在 Tower 中拥有了一个 Python 3 virtualenv 和一个 roles/requirements.yml 文件,您可以编写和测试 playbook。在 playbook 的第一个 play 中,定义名称、Ansible 要运行的目标主机、连接插件以及禁用网络设备的 gather_facts。接下来,创建一个 roles: 部分并调用 parse_genie 角色

---
- name: parser example
  hosts: ios
  gather_facts: no
  connection: network_cli
  roles:
    - parse_genie

然后创建 tasks: 部分并添加 show version 任务。这将通过 ios_command 模块执行 show version 命令,然后将输出存储到名为 version 的变量中。

tasks:
- name: show version
  ios_command:
    commands:
      - show version
    register: version

接下来的任务将应用 parse_genie 过滤器插件,以从我们执行的 show version 命令中创建结构化数据。以及将结构化数据设置为事实并对其进行调试。

- name: Set Fact Genie Filter
  set_fact:
    pyats_version: "{{ version['stdout'][0] | parse_genie(command='show version', os='ios') }}"

- name: Debug Genie Filter
  debug:
    var: pyats_version

步骤 4:运行 Ansible Playbook

此时 playbook 基本上已经完成,您可以执行并测试它。

---
- name: parser example
  hosts: ios
  gather_facts: no
  connection: network_cli
  roles:
    - parse_genie

tasks:
- name: show version
  ios_command:
    commands:
      - show version
  register: version

- name: Set Fact Genie Filter
  set_fact:
    pyats_version: "{{ version['stdout'][0] | parse_genie(command='show version', os='ios') }}"

- name: Debug Genie Filter
  debug:
    var: pyats_version

解析器获取命令输出并创建 JSON 格式的结构化数据。您稍后在 playbook 中要使用的信息现在很容易访问。

步骤 5:验证 Ansible Playbook 运行

运行 playbook 后(我们通过 Ansible Tower 运行),以下是 playbook 运行中的调试 Genie 过滤器任务

image6-2

完整输出

TASK [Debug Genie Filter] ******************************************************

ok: [192.168.161.9] => {
    "msg": {
        "version": {
            "chassis": "WS-C3550-24",
            "chassis_sn": "CAT0651Z1E8",
            "curr_config_register": "0x10F",
            "hostname": "nco-rtr-9",
            "image_id": "C3550-IPSERVICESK9-M",
            "image_type": "developer image",
            "last_reload_reason": "warm-reset",
            "main_mem": "65526",
            "number_of_intfs": {
                "FastEthernet": "24",
                "Gigabit Ethernet": "2"
            },
            "os": "C3550 boot loader",
            "platform": "C3550",
            "processor_type": "PowerPC",
            "rom": "Bootstrap program is C3550 boot loader",
            "rtr_type": "WS-C3550-24",
            "system_image": "flash:c3550-ipservicesk9-mz.122-44.SE3/c3550-ipservicesk9-mz.122-44.SE3.bin",
            "uptime": "44 minutes",
            "version": "12.2(44)SE3",
            "version_short": "12.2"
        }
       }
}

步骤 6:将解析后的内容集成到 ServiceNow 工单中

我现在想做的是在 ServiceNow 事件布局中添加一些新字段。让我们将版本、正常运行时间、主机名、平台、设备类型、序列号和上次重新加载原因等信息添加到 Ansible 创建的每个事件工单中。

在 ServiceNow Web 仪表板中,在**配置 > 表单布局**中添加这些新字段。

image2-6

现在,当您从本博文的第 1 部分运行 playbook 并将 table 参数设置为 incident 时。当您调试 incident.record 字典时,它现在应该包含您刚刚创建的新字段,例如 u_device_up_time、u_ios_version 等。

ServiceNow API 发送回的记录字典的片段

image4-3

我们可以在使用snow_record 模块的 Ansible Playbook 的 data 部分中使用这些新字段。以下是运行 show version 命令、解析输出并将参数添加到新字段中的完整 playbook

---
- name: create ticket with notes
  hosts: ios
  gather_facts: no
  connection: network_cli
  roles:
    - parse_genie

  tasks:
  - name: include vars
    include_vars: incident_vars.yml

  - name: show version
    ios_command:
      commands:
        - show version
    register: version

  - name: Set Fact Genie Filter
    set_fact:
      pyats_version: "{{ version['stdout'][0] | parse_genie(command='show version', os='ios') }}"

# Example 1 showing version information
  - name: Debug Pyats facts
    debug:
      var: pyats_version.version.version

# Example 2 showing uptime
  - name: Debug Pyats facts
    debug:
      var: pyats_version.version.uptime

  - name: Create an incident
    snow_record:
      state: present
      table: incident
      username: "{{ sn_username }}"
      password: "{{ sn_password }}"
      instance: "{{ sn_instance }}"
      data:
        priority: "{{ sn_priority}}"
        u_device_up_time: "{{ pyats_version.version.uptime }}"
        u_ios_version: "{{ pyats_version.version.version }}"
        u_hostname: "{{ pyats_version.version.hostname }}"
        u_platform: "{{ pyats_version.version.platform }}"
        u_device_type: "{{ pyats_version.version.rtr_type }}"
        u_serial_number: "{{ pyats_version.version.chassis_sn }}"
        u_last_reload_reason: "{{ pyats_version.version.last_reload_reason }}"
        short_description: "This ticket was created by Ansible"

  - debug: var=new_incident.record.number

上面提供了两个额外的调试示例,以展示如何处理返回的 pyATS 字典。使用结构化输出,使用键获取所需特定信息变得更加容易(例如,pyats_version.version.uptime 是返回系统正常运行时间的键)。完整字典在步骤 5 中提供。

以下屏幕截图显示了从 Red Hat Ansible Tower 中显示的 playbook 的输出

image3-3

新字段现在已填充到我们的 ServiceNow 事件工单中

image5 copy

在发生故障时,情况可能会变得混乱。我们都见过,在网络领域的某些日子里,工单的优先级会变得很低。创建和动态事实的自动化解决了这个问题,并允许工程师专注于故障。

最后的想法

类似这样的方法可能有助于您的组织逐步采用自动化。这些 Ansible Playbook 风险较低,因为它们不会修改任何配置,它们是只读的。这可能是网络工程师的一个很好的第一步,无需进行整体自动化甚至配置管理。您可以考虑替换过滤器插件中的 ios 条目以使用 network_cli 连接插件中引入的 ansible_network_os 变量。这样,您就可以在同一清单和 playbook 运行中针对 nxos、ios、junos 等运行。在本博文中,我们将其保留为 ios,以便于理解,如果这是您第一次看到它。

敬请关注本系列的第 3 部分 - 我们将介绍从 ServiceNow 到 Ansible Tower API 的集成。您可以在其中自动让 ServiceNow 执行 Ansible Playbook。