网络调试和故障排除指南

本节讨论如何在 Ansible 中调试和排除网络模块的故障。

如何进行故障排除

Ansible 网络自动化错误通常属于以下类别之一

身份验证问题:
  • 未正确指定凭据

  • 远程设备(网络交换机/路由器)未回退到其他身份验证方法

  • SSH 密钥问题

超时问题:
  • 在尝试提取大量数据时可能会发生

  • 实际上可能是身份验证问题

Playbook 问题:
  • 使用 delegate_to 而不是 ProxyCommand。有关更多信息,请参阅 网络代理指南

警告

unable to open shell

消息 unable to open shell 表示 ansible-connection 守护程序无法成功与远程网络设备通信。这通常表示存在身份验证问题。有关更多信息,请参阅本文档中的“身份验证和连接问题”部分。

启用网络日志记录以及如何读取日志文件

平台:所有平台

Ansible 包含日志记录功能,以帮助诊断和解决与 Ansible 网络模块相关的问题。

由于日志记录非常详细,因此默认情况下处于禁用状态。可以通过 Ansible 控制节点(即运行 ansible-playbook 的机器)上的 ANSIBLE_LOG_PATHANSIBLE_DEBUG 选项启用它。

在运行 ansible-playbook 之前,运行以下命令以启用日志记录

# Specify the location for the log file
export ANSIBLE_LOG_PATH=~/ansible.log
# Enable Debug
export ANSIBLE_DEBUG=True

# Run with 4*v for connection level verbosity
ansible-playbook -vvvv ...

Ansible 运行完成后,您可以检查在 Ansible 控制节点上创建的日志文件

less $ANSIBLE_LOG_PATH

2017-03-30 13:19:52,740 p=28990 u=fred |  creating new control socket for host veos01:22 as user admin
2017-03-30 13:19:52,741 p=28990 u=fred |  control socket path is /home/fred/.ansible/pc/ca5960d27a
2017-03-30 13:19:52,741 p=28990 u=fred |  current working directory is /home/fred/ansible/test/integration
2017-03-30 13:19:52,741 p=28990 u=fred |  using connection plugin network_cli
...
2017-03-30 13:20:14,771 paramiko.transport userauth is OK
2017-03-30 13:20:15,283 paramiko.transport Authentication (keyboard-interactive) successful!
2017-03-30 13:20:15,302 p=28990 u=fred |  ssh connection done, setting terminal
2017-03-30 13:20:15,321 p=28990 u=fred |  ssh connection has completed successfully
2017-03-30 13:20:15,322 p=28990 u=fred |  connection established to veos01 in 0:00:22.580626

从日志通知

  • p=28990ansible-connection 进程的 PID(进程 ID)

  • u=fred 是运行 Ansible 的用户,而不是您尝试连接到的远程用户

  • creating new control socket for host veos01:22 as user admin 主机:端口作为用户

  • control socket path is 磁盘上创建持久连接套接字的位置

  • using connection plugin network_cli 通知您正在使用持久连接

  • connection established to veos01 in 0:00:22.580626 获取远程设备 Shell 所花费的时间

注意

端口 None creating new control socket for host veos01:None

如果日志报告端口为 None,则表示正在使用默认端口。未来的 Ansible 版本将改进此消息,以便始终记录端口。

由于日志文件非常详细,因此您可以使用 grep 搜索特定信息。例如,在从 creating new control socket for host 行中识别出 pid 后,您可以搜索其他连接日志条目

grep "p=28990" $ANSIBLE_LOG_PATH

启用网络设备交互日志记录

平台:所有平台

Ansible 包含设备交互日志记录功能,以帮助诊断和解决与 Ansible 网络模块相关的问题。消息记录在 Ansible 配置文件中 log_path 配置选项指向的文件中,或者通过设置 ANSIBLE_LOG_PATH 来记录。

警告

设备交互消息包含在目标设备上执行的命令和返回的响应。由于此日志数据可能包含敏感信息(包括明文密码),因此默认情况下处于禁用状态。此外,为了防止意外泄露数据,在启用此设置的每个任务中都会显示警告,指定哪个主机启用了该设置以及数据记录的位置。

请务必充分了解启用此选项的安全隐患。可以通过在配置文件中设置或通过设置环境变量来全局启用设备交互日志记录,或者可以通过将特殊变量传递给任务来在每个任务的基础上启用。

在运行 ansible-playbook 之前,运行以下命令以启用日志记录

# Specify the location for the log file
export ANSIBLE_LOG_PATH=~/ansible.log

为给定任务启用设备交互日志记录

- name: get version information
  cisco.ios.ios_command:
    commands:
      - show version
  vars:
    ansible_persistent_log_messages: True

要将其设置为全局设置,请将以下内容添加到您的 ansible.cfg 文件中

[persistent_connection]
log_messages = True

或启用环境变量 ANSIBLE_PERSISTENT_LOG_MESSAGES

# Enable device interaction logging
export ANSIBLE_PERSISTENT_LOG_MESSAGES=True

如果任务在连接初始化本身失败,则应全局启用此选项。如果某个特定任务间歇性地失败,则可以为此任务本身启用此选项以查找根本原因。

Ansible 运行完成后,您可以检查在 Ansible 控制节点上创建的日志文件

注意

请务必充分了解启用此选项的安全隐患,因为它可能会将敏感信息记录到日志文件中,从而造成安全漏洞。

隔离错误

平台:所有平台

与任何故障排除工作一样,简化测试用例非常重要。

对于 Ansible,可以通过确保仅针对一台远程设备运行来实现

  • 使用 ansible-playbook --limit switch1.example.net...

  • 使用临时的 ansible 命令

“临时”(ad hoc)指的是使用 /usr/bin/ansible 执行一些快速命令,而不是使用编排语言 /usr/bin/ansible-playbook。在这种情况下,我们可以通过尝试在远程设备上执行单个命令来确保连接性。

ansible -m arista.eos.eos_command -a 'commands=?' -i inventory switch1.example.net -e 'ansible_connection=ansible.netcommon.network_cli' -u admin -k

在上面的示例中,我们

  • 连接到清单文件 inventory 中指定的 switch1.example.net

  • 使用模块 arista.eos.eos_command

  • 运行命令 ?

  • 使用用户名 admin 连接

  • 通过指定 -k 告知 ansible 命令提示输入 SSH 密码。

如果已正确配置 SSH 密钥,则无需指定 -k 参数。

如果连接仍然失败,可以将其与 enable_network_logging 参数结合使用。例如

# Specify the location for the log file
export ANSIBLE_LOG_PATH=~/ansible.log
# Enable Debug
export ANSIBLE_DEBUG=True
# Run with ``-vvvv`` for connection level verbosity
ansible -m arista.eos.eos_command -a 'commands=?' -i inventory switch1.example.net -e 'ansible_connection=ansible.netcommon.network_cli' -u admin -k

然后查看日志文件,并在本文档的其余部分中查找相关的错误消息。

排查套接字路径问题

平台:所有平台

Socket path does not exist or cannot be foundUnable to connect to socket 消息表明用于与远程网络设备通信的套接字不可用或不存在。

例如

fatal: [spine02]: FAILED! => {
    "changed": false,
    "failed": true,
    "module_stderr": "Traceback (most recent call last):\n  File \"/tmp/ansible_TSqk5J/ansible_modlib.zip/ansible/module_utils/connection.py\", line 115, in _exec_jsonrpc\nansible.module_utils.connection.ConnectionError: Socket path XX does not exist or cannot be found. See Troubleshooting socket path issues in the Network Debug and Troubleshooting Guide\n",
    "module_stdout": "",
    "msg": "MODULE FAILURE",
    "rc": 1
}

fatal: [spine02]: FAILED! => {
    "changed": false,
    "failed": true,
    "module_stderr": "Traceback (most recent call last):\n  File \"/tmp/ansible_TSqk5J/ansible_modlib.zip/ansible/module_utils/connection.py\", line 123, in _exec_jsonrpc\nansible.module_utils.connection.ConnectionError: Unable to connect to socket XX. See Troubleshooting socket path issues in Network Debug and Troubleshooting Guide\n",
    "module_stdout": "",
    "msg": "MODULE FAILURE",
    "rc": 1
}

解决建议

  1. 验证您是否对错误消息中描述的套接字路径具有写访问权限。

  2. 按照 启用网络日志记录 中详细介绍的步骤操作。

如果日志文件中识别的错误消息为

2017-04-04 12:19:05,670 p=18591 u=fred |  command timeout triggered, timeout value is 30 secs

2017-04-04 12:19:05,670 p=18591 u=fred |  persistent connection idle timeout triggered, timeout value is 30 secs

按照 超时问题 中详细介绍的步骤操作。

类别“无法打开 Shell”

平台:所有平台

unable to open shell 消息表示 ansible-connection 守护程序无法成功与远程网络设备通信。这通常意味着存在身份验证问题。这是一个“万能”消息,这意味着您需要启用 日志记录 以查找根本原因。

例如

TASK [prepare_eos_tests : enable cli on remote device] **************************************************
fatal: [veos01]: FAILED! => {"changed": false, "failed": true, "msg": "unable to open shell"}

TASK [ios_system : configure name_servers] *************************************************************
task path:
fatal: [ios-csr1000v]: FAILED! => {
    "changed": false,
    "failed": true,
    "msg": "unable to open shell",
}

解决建议

按照 enable_network_logging 中详细介绍的步骤操作。

一旦您从日志文件中识别出错误消息,就可以在本文档的其余部分中找到具体的解决方案。

错误:“[Errno -2] 名称或服务未知”

平台:所有平台

表示无法访问您尝试连接的远程主机。

例如

2017-04-04 11:39:48,147 p=15299 u=fred |  control socket path is /home/fred/.ansible/pc/ca5960d27a
2017-04-04 11:39:48,147 p=15299 u=fred |  current working directory is /home/fred/git/ansible-inc/stable-2.3/test/integration
2017-04-04 11:39:48,147 p=15299 u=fred |  using connection plugin network_cli
2017-04-04 11:39:48,340 p=15299 u=fred |  connecting to host veos01 returned an error
2017-04-04 11:39:48,340 p=15299 u=fred |  [Errno -2] Name or service not known

解决建议

  • 如果您使用的是 provider: 选项,请确保其子选项 host: 设置正确。

  • 如果您未使用 provider: 或顶级参数,请确保您的清单文件正确。

错误:“身份验证失败”

平台:所有平台

如果传递给 ansible-connection (通过 ansibleansible-playbook)的凭据(用户名、密码或 SSH 密钥)无法用于连接到远程设备,则会发生此错误。

例如

<ios01> ESTABLISH CONNECTION FOR USER: cisco on PORT 22 TO ios01
<ios01> Authentication failed.

解决建议

如果您通过 password: (直接或通过 provider:)或环境变量 ANSIBLE_NET_PASSWORD 指定凭据,则 paramiko (Ansible 使用的 Python SSH 库)可能正在使用 SSH 密钥,因此您指定的凭据将被忽略。要确定是否为这种情况,请禁用“查找密钥”。可以通过以下方式实现

export ANSIBLE_PARAMIKO_LOOK_FOR_KEYS=False

要将其设置为永久更改,请将以下内容添加到您的 ansible.cfg 文件中

[paramiko_connection]
look_for_keys = False

错误:“连接到主机<hostname>返回错误”或“错误的地址”

如果 SSH 指纹尚未添加到 Paramiko(Python SSH 库)的 known_hosts 文件中,则可能会发生这种情况。

在使用 Paramiko 的持久连接时,连接在后台进程中运行。如果主机尚不存在有效的 SSH 密钥,则默认情况下,Ansible 将提示添加主机密钥。这将导致在后台进程中运行的连接失败。

例如

2017-04-04 12:06:03,486 p=17981 u=fred |  using connection plugin network_cli
2017-04-04 12:06:04,680 p=17981 u=fred |  connecting to host veos01 returned an error
2017-04-04 12:06:04,682 p=17981 u=fred |  (14, 'Bad address')
2017-04-04 12:06:33,519 p=17981 u=fred |  number of connection attempts exceeded, unable to connect to control socket
2017-04-04 12:06:33,520 p=17981 u=fred |  persistent_connect_interval=1, persistent_connect_retries=30

解决建议

使用 ssh-keyscan 预填充 known_hosts。您需要确保密钥正确。

ssh-keyscan veos01

您可以告诉 Ansible 自动接受密钥

环境变量方法

export ANSIBLE_PARAMIKO_HOST_KEY_AUTO_ADD=True
ansible-playbook ...

ansible.cfg 方法

ansible.cfg

[paramiko_connection]
host_key_auto_add = True

错误:“没有可用的身份验证方法”

例如

2017-04-04 12:19:05,670 p=18591 u=fred |  creating new control socket for host veos01:None as user admin
2017-04-04 12:19:05,670 p=18591 u=fred |  control socket path is /home/fred/.ansible/pc/ca5960d27a
2017-04-04 12:19:05,670 p=18591 u=fred |  current working directory is /home/fred/git/ansible-inc/ansible-workspace-2/test/integration
2017-04-04 12:19:05,670 p=18591 u=fred |  using connection plugin network_cli
2017-04-04 12:19:06,606 p=18591 u=fred |  connecting to host veos01 returned an error
2017-04-04 12:19:06,606 p=18591 u=fred |  No authentication methods available
2017-04-04 12:19:35,708 p=18591 u=fred |  connect retry timeout expired, unable to connect to control socket
2017-04-04 12:19:35,709 p=18591 u=fred |  persistent_connect_retry_timeout is 15 secs

解决建议

没有提供密码或 SSH 密钥。

清除持久连接

平台:所有平台

在 Ansible 2.3 中,持久连接套接字存储在 ~/.ansible/pc 中,用于所有网络设备。当 Ansible playbook 运行时,在指定详细输出时会显示持久套接字连接。

<switch> socket_path: /home/fred/.ansible/pc/f64ddfa760

要在持久连接超时之前清除它(默认超时为 30 秒的空闲时间),只需删除套接字文件。

超时问题

持久连接空闲超时

默认情况下,ANSIBLE_PERSISTENT_CONNECT_TIMEOUT 设置为 30(秒)。如果此值过低,您可能会看到以下错误

2017-04-04 12:19:05,670 p=18591 u=fred |  persistent connection idle timeout triggered, timeout value is 30 secs

解决建议

增加持久连接空闲超时的值。

export ANSIBLE_PERSISTENT_CONNECT_TIMEOUT=60

要将其设置为永久更改,请将以下内容添加到您的 ansible.cfg 文件中

[persistent_connection]
connect_timeout = 60

命令超时

默认情况下,ANSIBLE_PERSISTENT_COMMAND_TIMEOUT 设置为 30(秒)。早期版本的 Ansible 将此值默认设置为 10 秒。如果此值过低,您可能会看到以下错误

2017-04-04 12:19:05,670 p=18591 u=fred |  command timeout triggered, timeout value is 30 secs

解决建议

  • 选项 1(全局命令超时设置):在配置文件中或通过设置环境变量来增加命令超时的值。

    export ANSIBLE_PERSISTENT_COMMAND_TIMEOUT=60
    

    要将其设置为永久更改,请将以下内容添加到您的 ansible.cfg 文件中

    [persistent_connection]
    command_timeout = 60
    
  • 选项 2(每个任务的命令超时设置):按任务增加命令超时。所有网络模块都支持可以在每个任务的基础上设置的超时值。超时值控制命令在未返回之前任务失败之前的时间(以秒为单位)。

    对于本地连接类型

    解决建议

    某些模块支持 timeout 选项,它与任务的 timeout 关键字不同。

    - name: save running-config
      cisco.ios.ios_command:
        commands: copy running-config startup-config
        provider: "{{ cli }}"
        timeout: 30
    

    解决建议

    如果模块不支持 timeout 选项,大多数网络连接插件可以使用 ansible_command_timeout 变量启用类似的功能。

    - name: save running-config
      cisco.ios.ios_command:
        commands: copy running-config startup-config
      vars:
        ansible_command_timeout: 60
    

某些操作需要超过默认的 30 秒才能完成。一个很好的例子是在 IOS 设备上将当前运行的配置保存到启动配置。在这种情况下,将超时值从默认的 30 秒更改为 60 秒将防止任务在命令成功完成之前失败。

持久连接重试超时

默认情况下,ANSIBLE_PERSISTENT_CONNECT_RETRY_TIMEOUT 设置为 15(秒)。如果此值过低,您可能会看到以下错误

2017-04-04 12:19:35,708 p=18591 u=fred |  connect retry timeout expired, unable to connect to control socket
2017-04-04 12:19:35,709 p=18591 u=fred |  persistent_connect_retry_timeout is 15 secs

解决建议

增加持久连接空闲超时的值。注意:此值应大于 SSH 超时值(配置文件中 defaults 部分下的超时值),并且小于持久连接空闲超时值(connect_timeout)。

export ANSIBLE_PERSISTENT_CONNECT_RETRY_TIMEOUT=30

要将其设置为永久更改,请将以下内容添加到您的 ansible.cfg 文件中

[persistent_connection]
connect_retry_timeout = 30

由于具有 network_cli 连接类型的平台特定登录菜单导致的超时问题

在 Ansible 2.9 及更高版本中,添加了 network_cli 连接插件配置选项以处理平台特定的登录菜单。这些选项可以设置为组/主机或任务变量。

示例:使用主机变量处理单个登录菜单提示

$ cat host_vars/<hostname>.yaml
---
ansible_terminal_initial_prompt:
  - "Connect to a host"
ansible_terminal_initial_answer:
  - "3"

示例:使用主机变量处理远程主机多个登录菜单提示

$ cat host_vars/<inventory-hostname>.yaml
---
ansible_terminal_initial_prompt:
  - "Press any key to enter main menu"
  - "Connect to a host"
ansible_terminal_initial_answer:
  - "\\r"
  - "3"
ansible_terminal_initial_prompt_checkall: True

要处理多个登录菜单提示

  • ansible_terminal_initial_promptansible_terminal_initial_answer 的值应为列表。

  • 提示序列应与答案序列匹配。

  • ansible_terminal_initial_prompt_checkall 的值应设置为 True

注意

如果在连接初始化时没有从远程主机收到序列中的所有提示,则会导致超时。

Playbook 问题

本节详细说明了由 Playbook 本身问题引起的问题。

错误:“无法进入配置模式”

平台:Arista EOS 和 Cisco IOS

当您尝试在用户模式 Shell 中运行需要特权模式的任务时,就会发生这种情况。

例如

TASK [ios_system : configure name_servers] *****************************************************************************
task path:
fatal: [ios-csr1000v]: FAILED! => {
    "changed": false,
    "failed": true,
   "msg": "unable to enter configuration mode",
}

解决建议

使用 connection: ansible.netcommon.network_clibecome: true

代理问题

delegate_to 与 ProxyCommand

为了使用堡垒机或中间跳板主机通过 cli 传输连接到网络设备,网络模块支持使用 ProxyCommand

要使用 ProxyCommand,请在 Ansible 清单文件中配置代理设置以指定代理主机。

[nxos]
nxos01
nxos02

[nxos:vars]
ansible_ssh_common_args='-o ProxyCommand="ssh -W %h:%p -q bastion01"'

使用上述配置,只需像往常一样构建和运行 playbook,无需进行任何其他更改。网络模块现在将首先连接到 ansible_ssh_common_args 中指定的主机(在上例中为 bastion01),然后连接到网络设备。

您还可以使用环境变量为所有主机设置代理目标。

export ANSIBLE_SSH_ARGS='-o ProxyCommand="ssh -W %h:%p -q bastion01"'

使用堡垒机/跳板主机与 netconf 连接

启用跳板主机设置

可以通过以下方式启用使用堡垒机/跳板主机与 netconf 连接:
  • 将 Ansible 变量 ansible_netconf_ssh_config 设置为 True 或自定义 ssh 配置文件路径

  • 将环境变量 ANSIBLE_NETCONF_SSH_CONFIG 设置为 True 或自定义 ssh 配置文件路径

  • netconf_connection 部分下设置 ssh_config = 1ssh_config = <ssh-file-path>

如果配置变量设置为 1,则从默认 ssh 配置文件 (~/.ssh/config) 读取 proxycommand 和其他 ssh 变量。

如果配置变量设置为文件路径,则从给定的自定义 ssh 文件路径读取 proxycommand 和其他 ssh 变量。

示例 ssh 配置文件 (~/.ssh/config)

Host jumphost
  HostName jumphost.domain.name.com
  User jumphost-user
  IdentityFile "/path/to/ssh-key.pem"
  Port 22

# Note: Due to the way that Paramiko reads the SSH Config file,
# you need to specify the NETCONF port that the host uses.
# In other words, it does not automatically use ansible_port
# As a result you need either:

Host junos01
  HostName junos01
  ProxyCommand ssh -W %h:22 jumphost

# OR

Host junos01
  HostName junos01
  ProxyCommand ssh -W %h:830 jumphost

# Depending on the netconf port used.

示例 Ansible 清单文件

[junos]
junos01

[junos:vars]
ansible_connection=ansible.netcommon.netconf
ansible_network_os=junipernetworks.junos.junos
ansible_user=myuser
ansible_password=!vault...

注意

通过变量使用 ProxyCommand 和密码

根据设计,SSH 不支持通过环境变量提供密码。这样做是为了防止机密信息泄露,例如在 ps 输出中。

我们建议尽可能使用 SSH 密钥,如果需要,还可以使用 ssh-agent,而不是密码。

其他问题

使用 ansible.netcommon.network_cli 连接类型时出现间歇性故障

如果在 ansible.netcommon.network_cli 连接插件中没有正确匹配响应中收到的命令提示符,则任务可能会间歇性地失败,并出现截断的响应或错误消息 operation requires privilege escalation。从 2.7.1 开始,添加了一个新的缓冲区读取计时器以确保正确匹配提示并以输出形式发送完整的响应。计时器的默认值为 0.2 秒,可以在每个任务的基础上调整,也可以以秒为单位全局设置。

示例:每个任务计时器设置

- name: gather ios facts
  cisco.ios.ios_facts:
    gather_subset: all
  register: result
  vars:
    ansible_buffer_read_timeout: 2

要将其设置为全局设置,请将以下内容添加到您的 ansible.cfg 文件中

[persistent_connection]
buffer_read_timeout = 2

可以通过将值设置为零来禁用在远程主机上执行的每个命令的此计时器延迟。

由于命令响应中错误正则表达式不匹配导致任务失败,使用 ansible.netcommon.network_cli 连接类型

在 Ansible 2.9 及更高版本中,添加了 ansible.netcommon.network_cli 连接插件配置选项以处理 stdout 和 stderr 正则表达式,以识别命令执行响应是否包含正常响应或错误响应。这些选项可以设置为组/主机变量或作为任务变量。

示例:错误响应不匹配

- name: fetch logs from remote host
  cisco.ios.ios_command:
    commands:
      - show logging

Playbook 运行输出

TASK [first fetch logs] ********************************************************
fatal: [ios01]: FAILED! => {
    "changed": false,
    "msg": "RF Name:\r\n\r\n <--nsip-->
           \"IPSEC-3-REPLAY_ERROR: Test log\"\r\n*Aug  1 08:36:18.483: %SYS-7-USERLOG_DEBUG:
            Message from tty578(user id: ansible): test\r\nan-ios-02#"}

解决建议

修改单个任务的错误正则表达式。

- name: fetch logs from remote host
  cisco.ios.ios_command:
    commands:
      - show logging
  vars:
    ansible_terminal_stderr_re:
      - pattern: 'connection timed out'
        flags: 're.I'

终端插件正则表达式选项 ansible_terminal_stderr_reansible_terminal_stdout_re 具有 patternflags 作为键。 flags 键的值应为 re.compile python 方法接受的值。

由于网络或远程目标主机速度较慢导致使用 ansible.netcommon.network_cli 连接类型时出现间歇性故障

在 Ansible 2.9 及更高版本中,添加了 ansible.netcommon.network_cli 连接插件配置选项来控制连接到远程主机的尝试次数。默认尝试次数为三次。在每次重试尝试后,重试之间的延迟以秒为单位以 2 的幂增加,直到尝试次数达到最大值或 persistent_command_timeoutpersistent_connect_timeout 计时器触发。

要将其设置为全局设置,请将以下内容添加到您的 ansible.cfg 文件中

[persistent_connection]
network_cli_retries = 5