自动化运维,你玩转了吗?
【摘要】
上一章节中已经介绍了如何使用Netmiko创建连接,发送命令,并了解了Netmiko如何发送命令并接收数据,但只掌握这些还是远远不够的,实际的场景中会遇到各种各样的问题,诸如:设备类型问题,连接超时问题,执行超时问题等,这些问题其实Netmiko都有办法通过参数的调整来解决。
先回顾一下基础的代码,如下所示:
from netmiko import ConnectHandler
params = {
"device_type": "cisco_ios",
"host": "192.168.31.149",
"username": "cisco",
"password": "cisco"
}
net_connect = ConnectHandler(**params)
cmd = "show interface brief"
output = net_connect.send_command(cmd)
print(output)
【连接参数】
Netmiko中支持的不同厂商的连接对象ConnectionClass
都是继承自一个基类BaseConnection
,并且常见的几种厂商初始化连接对象的方式(即__init__
方法)几乎都没有进行重写,也就是说我们目前只需要着重了解基类的初始化即可。
源码中的连接对象初始化参数如下:
def __init__(
self,
ip: str = "",
host: str = "",
username: str = "",
password: Optional[str] = None,
secret: str = "",
port: Optional[int] = None,
device_type: str = "",
verbose: bool = False,
global_delay_factor: float = 1.0,
global_cmd_verify: Optional[bool] = None,
use_keys: bool = False,
key_file: Optional[str] = None,
pkey: Optional[paramiko.PKey] = None,
passphrase: Optional[str] = None,
disabled_algorithms: Optional[Dict[str, Any]] = None,
allow_agent: bool = False,
ssh_strict: bool = False,
system_host_keys: bool = False,
alt_host_keys: bool = False,
alt_key_file: str = "",
ssh_config_file: Optional[str] = None,
#
# Connect timeouts
# ssh-connect --> TCP conn (conn_timeout) --> SSH-banner (banner_timeout)
# --> Auth response (auth_timeout)
conn_timeout: int = 10,
# Timeout to wait for authentication response
auth_timeout: Optional[int] = None,
banner_timeout: int = 15, # Timeout to wait for the banner to be presented
# Other timeouts
blocking_timeout: int = 20, # Read blocking timeout
timeout: int = 100, # TCP connect timeout | overloaded to read-loop timeout
session_timeout: int = 60, # Used for locking/sharing the connection
read_timeout_override: Optional[float] = None,
keepalive: int = 0,
default_enter: Optional[str] = None,
response_return: Optional[str] = None,
serial_settings: Optional[Dict[str, Any]] = None,
fast_cli: bool = True,
_legacy_mode: bool = False,
session_log: Optional[SessionLog] = None,
session_log_record_writes: bool = False,
session_log_file_mode: str = "write",
allow_auto_change: bool = False,
encoding: str = "utf-8",
sock: Optional[socket.socket] = None,
auto_connect: bool = True,
delay_factor_compat: bool = False,
) -> None:
大部分朋友可能看到这么多参数会非常头疼,我这里给大家做一个归类,就可以很好的掌握这些参数的作用了。
参数分类
通过上面表格的分类,大家应该都对连接对象的参数有了清晰的认识,虽然每个分类里的参数都比较多,但并不是都需要全部用到,我这里只把常用的几个参数通过高亮标识里出来。
参数调整
连接设备必须的基本参数和认证参数就不做赘述了,大家可以根据自己的实际情况选择是用户名/密码方式连接,还是密钥连接。
但超时参数里有几个参数是比较重要的。
conn_timeout auth_timeout banner_timeout
整个连接的建立过程存在几个可能会超时的节点,如下图所示:

所以大家可以根据连接设备时具体的报错日志分析,超时异常出现在哪一个阶段,然后相应的调整连接超时参数即可。
【命令参数】
Netmiko发送命令的函数有很多个,分别适用于不同的场景,但今天我们以最为常用的send_command
做为例子来讲解,因为其他的执行函数大多也都是基于该函数进行了封装,或者其参数与该函数的参数也基本类似。
源码中函数的参数如下:
def send_command(
self,
command_string: str,
expect_string: Optional[str] = None,
read_timeout: float = 10.0,
delay_factor: Optional[float] = None,
max_loops: Optional[int] = None,
auto_find_prompt: bool = True,
strip_prompt: bool = True,
strip_command: bool = True,
normalize: bool = True,
use_textfsm: bool = False,
textfsm_template: Optional[str] = None,
use_ttp: bool = False,
ttp_template: Optional[str] = None,
use_genie: bool = False,
cmd_verify: bool = True,
) -> Union[str, List[Any], Dict[str, Any]]:
其实源码中的类,函数,参数的作用都和实际的命名相关,所以大家可以多去翻译一下,这样不仅可以在后续编程的过程中养成良好的命名习惯,也可以慢慢的面对陌生的函数或参数大概猜到其用法。
参数分类
参数调整
执行的命令的参数中大多数都是默认设置,并且都设为了True,也就是说大多数情况下都不需要修改;而需要根据实际情况修改的参数我也高亮标注了出来。
read_timeout
这个函数之前已经提到了一次,在Netmiko5.x中可以直接用这个参数来调整循环读取通道的超时时间,不过通常的做法是设置一个较大的值,使其可以覆盖大多数的命令延迟,比如设为100。
如果实际确实有延迟很高的命令的话,再根据具体的命令适当修改read_timeout的值。
可能部分朋友会觉得直接设置一个5分钟或者10分钟,这样就可以覆盖所有命令了,
没必要单独处理。
但如果真的某台设备出现故障,所有的命令都延迟极高,或者网络链路出现很大抖动,
那么执行的命令会在一段时间内都获取不到结果,这种情况下超时时间都设为5分钟的话,
就无法及时抛出超时异常,而是会陷入长时间的循环等待,这当然不是我们想要看到的。
所以对于异常情况,或者边界case的考虑是需要大家在学习和运用的过程中锻炼提高的。
expect_string
由于auto_find_prompt
参数默认设为了True,所以即使不传递expect_string,也可以自动获取到设备标识符做为search_pattern
。
对于特殊的命令的话,我们可以修改该参数,使其作为判定循环读取结束的正则表达式,但大多数命令其实都是用的设备标识符做的search_pattern
。
所以我的建议是这里直接将expect_string
设置为self.base_prompt,或者将auto_find_prompt设置为False
,因为在连接建立后的session_preparation
函数里获取了设备标识符作为base_prompt
,所以send_command
函数自动获取的标识符同样也是发送换行符后获取的设备标识符,设置调用的函数都是同一个find_prompt。
所以我们大多数情况下可以直接传入self.base_prompt
作为search_pattern
,这样就可以每条命令都减少一次自动获取标识符时产生的与设备的交互,对于提高性能来说会有不小的帮助。
【基于时间的命令执行】
上面提到send_command
是使用最为普遍的一个执行命令函数,它是基于字符串匹配决定是否输出结束,但对于部分命令,我们无法准确的定义它的expect_string
,这时候我们就需要另一种方式来获取它的执行结果,那就是send_command_timing
,这个函数是一种基于时间机制的命令执行函数。
参数
这个函数的参数与send_command
中的参数几乎完全一致,只额外多了一个last_read
参数,这个参数会对是否结束循环起到关键性的作用。
除此之外,read_timeout
时间也从默认的100s改为了默认120s。
流程图

基于时间的机制中,循环读取通道数据后,已经不需要设置last_n_reads
队列来提高性能来,因为时间机制下,完全没有做任何的字符匹配,而是单纯通过last_read
这一变量来判断是否输出结束。
循环读取通道时,当此次没有读取到任何数据,那么就说明有可能已经输出结束了,那么此时休眠last_read
秒之后再读取一次,如果还没有数据,就判定为结束,如果又读到了数据,那么上一次没读到数据就有可能是设备或网络延迟造成的,则再次进入循环。
调参
send_command_timing
是基于时间机制来判定输出结束的,所以我们能调整的就是两个参数:
read_timeout
当执行的命令返回内容输出较长时,则需要循环更多次才能读取到全部结果,这时候就需要调大read_timeout
,避免抛出超时异常。
last_read
当执行的命令需要较长时间延迟才能输出回显结果时,可能会造成某次循环在last_read
时间内从通道中读取不到任何数据,导致提前结束循环,获取不到全部的结果,所以这时候需要调大last_read
参数。
【执行命令(配置)】
对于网络设备来说,在不同的模式下可以执行不同的命令,show类型的命令通常都在用户模式或特权模式执行,而配置命令的话必须要在配置模式执行;
那么对于不同的设备进入配置模式的方式也都不尽相同,既然Netmiko能够支持不同厂商的设备,就理所应当也适配了该类型设备进入配置模式的方法。
关于配置模式相关的函数如下:
self.check_config_mode: 调用该函数可以检查当前是否在配置模式下,返回一个bool值 self.config_mode:调用函数可以进入设备的配置模式 self.exit_config_mode: 调用该函数可以退出配置模式
通常执行配置类型的命令需要:进入配置模式 -> 执行命令 -> 退出配置模式,而Netmiko就为我们提供了一个封装好的方法:
send_config_set
源码中的参数如下:
def send_config_set(
self,
config_commands: Union[str, Sequence[str], Iterator[str], TextIO, None] = None,
*,
exit_config_mode: bool = True,
read_timeout: Optional[float] = None,
delay_factor: Optional[float] = None,
max_loops: Optional[int] = None,
strip_prompt: bool = False,
strip_command: bool = False,
config_mode_command: Optional[str] = None,
cmd_verify: bool = True,
enter_config_mode: bool = True,
error_pattern: str = "",
terminator: str = r"#",
bypass_commands: Optional[str] = None,
) -> str:
参数
上述参数虽然比较多,但都较好理解,部分参数我也会在视频中更通俗易懂的讲解一下,但这个函数封装的比较完善,绝大多数情况都只需要传递config_command
即可,该参数的值可以是字符串,或数组。
【总结】
这一章节主要是对Netmiko中的核心函数的讲解,这些函数的参数也都给大家分类展示了出来,更容易理解和使用,但还是需要从实践中加深理解;而且Netmiko的封装过程都是基于网络设备的交互逻辑,所以大家也不需要死记硬背,只要结合网络运维的经验即可理解掌握。
下一章节我会给大家代入一个简单的巡检场景,讲解如何真正编写一个健壮的与网络设备交互的代码。




