2. 网络设备交互基础

网络自动化的第一步,是让 Python 程序能够与网络设备“说上话”。不同协议决定了通信的通道、认证方式、数据格式和可操作范围。理解这些差异,才能在实际运维中选择合适的连接方式,并正确处理交互式命令与配置模式切换。

四种主流交互协议的核心差异

Telnet 是最早的远程登录协议,基于 TCP 23 端口,所有数据(包括用户名和密码)以明文传输。它的优势是实现简单、兼容性极好,几乎所有网络设备都支持;缺点是安全性差,只适合实验室或隔离管理网络。Python 标准库 telnetlib 可直接使用,但该库在 Python 3.13 中已被移除,实际工程中更推荐用 socket 自行封装或使用第三方库。

SSH 是目前主流的远程管理协议,默认 TCP 22 端口,通过加密通道传输数据,支持密码和密钥认证。网络设备上的 SSH 通常提供交互式 shell,也支持执行单条命令。Python 中 paramiko 是最常用的 SSH 库,它封装了传输层加密、认证、通道和会话,适合批量登录设备执行命令。

NETCONF 是基于 SSH 的应用层协议,默认使用 830 端口,采用 XML 编码,通过 <rpc> 和 <rpc-reply> 消息交换配置与状态数据。它面向“配置数据库”模型,支持 candidate、running、startup 等数据存储,适合结构化配置管理。Python 中常用 ncclient 库。

RESTCONF 则把 NETCONF 的数据模型映射到 HTTP 方法上,使用 GET、POST、PUT、PATCH、DELETE 操作 YANG 模型数据,返回 JSON 或 XML。它更适合与 Web 系统、控制器平台集成,Python 中可用 requests 直接调用。

Python 与网络设备交互协议栈对照 Python 程序侧 socket TCP 原始连接 paramiko SSH 客户端 ncclient NETCONF 客户端 requests HTTP/RESTCONF telnetlib / socket Telnet 明文 协议通道与端口 TCP 23 · Telnet 明文传输,无加密 TCP 22 · SSH 加密通道,交互式 Shell TCP 830 · NETCONF over SSH XML RPC,配置数据库 TCP 443 · RESTCONF HTTPS + JSON/XML YANG 数据模型 NETCONF / RESTCONF 共同基础 网络设备侧 VTY / Console 用户视图 / 特权视图 SSH Server 认证 + 加密会话 NETCONF Server candidate/running RESTCONF API HTTP 方法映射 配置存储 running / startup 选择原则:安全优先选 SSH/NETCONF/RESTCONF;结构化配置优先 NETCONF/RESTCONF;兼容老旧设备才考虑 Telnet

用 socket 理解连接的本质

无论上层协议多复杂,底层都是 TCP 连接。socket 模块让我们直接观察“连接—发送—接收—关闭”这一过程。下面示例连接一台设备的 22 端口,仅验证可达性,不进行 SSH 协商:

import socket

host = "192.168.1.1"
port = 22

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(5)

try:
    s.connect((host, port))
    banner = s.recv(1024)
    print("Banner:", banner.decode(errors="ignore").strip())
except socket.timeout:
    print("连接超时,检查 IP、端口或 ACL")
except ConnectionRefusedError:
    print("端口拒绝连接,服务可能未开启")
finally:
    s.close()

运行后若返回类似 SSH-2.0-Cisco-1.25 的 banner,说明 TCP 可达且 SSH 服务已监听。这一步常被用作批量巡检的前置探测:先判断设备是否在线、端口是否开放,再决定是否发起完整 SSH 会话。

paramiko 建立 SSH 会话与命令执行

paramiko 的核心对象是 SSHClient。它负责建立传输、认证、打开通道。下面示例使用密码认证登录设备,执行 display version 并读取输出:

import paramiko
import time

client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())

client.connect(
    hostname="192.168.1.1",
    port=22,
    username="admin",
    password="Admin@123",
    timeout=10,
    look_for_keys=False,
    allow_agent=False
)

shell = client.invoke_shell()
time.sleep(1)
shell.recv(65535)

shell.send("display version\n")
time.sleep(2)
output = shell.recv(65535).decode(errors="ignore")
print(output)

client.close()

这里使用 invoke_shell() 而不是 exec_command(),是因为网络设备通常需要进入交互式 shell 才能切换视图。注意 time.sleep() 只是教学简化,生产环境应使用 recv_ready() 轮询或 select 等待数据,避免因设备响应慢导致读取不完整。

交互式命令与配置模式切换

网络设备的命令行通常分为用户视图、系统视图、接口视图等多层模式。以华为设备为例,提示符从 <Huawei> 变为 [Huawei] 表示进入系统视图,再进入接口视图变为 [Huawei-GigabitEthernet0/0/1]。Python 脚本必须根据提示符判断当前模式,再发送对应命令。

一个实用的模式切换与配置下发片段如下:

def send_cmd(shell, cmd, wait=1.5):
    shell.send(cmd + "\n")
    time.sleep(wait)
    return shell.recv(65535).decode(errors="ignore")

send_cmd(shell, "system-view")
send_cmd(shell, "interface GigabitEthernet0/0/1")
send_cmd(shell, "description Python_Auto_Test")
send_cmd(shell, "return")
send_cmd(shell, "save", wait=3)

判断配置是否成功,不能只看有没有回显,而要检查返回内容中是否出现 Error、Unrecognized、Invalid 等关键字,同时确认提示符已回到预期层级。批量运维中,建议把“发送命令—等待—读取—校验”封装为统一函数,并对不同厂商维护提示符映射表。

实践建议:首次连接设备时,先用 socket 探测端口,再用 paramiko 建立会话;执行配置前先备份 running-config,配置后通过 display current-configuration interface 回读校验,形成“探测—连接—执行—回读”的闭环。
注意:示例中的 IP、用户名、密码均为教学假设,实际使用时请替换为真实环境参数,并避免将密码硬编码在脚本中,建议通过环境变量或密钥管理服务注入。