网络调试和故障排除指南

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

如何进行故障排除

Ansible 网络自动化错误通常归类于以下几种情况:

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

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

  • SSH 密钥问题

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

  • 实际上可能掩盖了身份验证问题

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

警告

无法 打开 shell

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

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

平台:任何平台

Ansible 包含日志记录功能,有助于诊断和排查与 Ansible 网络模块有关的问题。

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

在运行 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...

  • 使用 ad hoc ansible 命令

ad hoc 指的是使用 /usr/bin/ansible 运行 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 库)的已知主机文件中,则可能会出现此问题。

在使用 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 剧本运行时,如果指定了详细输出,则会显示持久套接字连接。

<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

注意

如果在连接初始化时未从远程主机依次接收到所有提示,将导致超时。

剧本问题

本节详细介绍了由剧本本身问题引起的问题。

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

平台: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"'

使用上面的配置,只需像往常一样构建和运行剧本,无需进行任何其他更改。现在,网络模块将通过首先连接到在 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 配置文件(~/.ssh/config)中读取。

如果配置变量设置为文件路径,则代理命令和其他 ssh 变量将从给定的自定义 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

剧本运行输出

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 连接插件配置选项以控制连接到远程主机的尝试次数。默认尝试次数为 3。在每次重试尝试后,重试之间的延迟将以秒为单位增加 2 的幂,直到尝试次数达到最大值或 persistent_command_timeoutpersistent_connect_timeout 计时器被触发为止。

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

[persistent_connection]
network_cli_retries = 5