自动化运维初级村-Netmiko-进阶
摘要
上一章节中已经介绍了如何使用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:
大部分朋友可能看到这么多参数会非常头疼,我这里给大家做一个归类,就可以很好的掌握这些参数的作用了。
参数分类
类别 | 参数名 | 参数类型 | 默认值 | 含义 |
---|---|---|---|---|
基本参数 | ip/host | string | "" | 两者必须填一个,作为远程设备的地址 |
device_type | string | "" | 根据设备类型映射到对应的连接对象 | |
认证参数 | username | string | "" | 对目标设备进行身份验证的用户名 |
password | string | None | 对目标设备进行身份验证的密码 | |
secret | string | "" | 如果目标设备需要,则设置为enable的密码 | |
use_keys | bool | False | 使用SSH密钥连接目标设备 | |
key_file | string | None | 要使用的SSH密钥文件的文件名路径 | |
pkey | paramiko.PKey | None | 要使用的SSH密钥对象 | |
passphrase | string | None | 用于密钥的密码短语 | |
disabled_algorithms | dict | None | 要禁用的SSH算法字典 | |
allow_agent | bool | False | 启用SSH key-agent | |
ssh_strict | bool | False | 自动拒绝未知的SSH主机密钥(默认值:False表示将接受未知的SSH主机密钥)。 | |
system_host_keys | bool | False | 从users known_hosts文件加载主机密钥 | |
alt_host_keys | bool | False | 如果' True '主机密钥将从指定的文件加载alt_key_file。 | |
alt_key_file | string | "" | 要使用的SSH主机密钥文件(如果alt_host_keys=True) | |
ssh_config_file | string | "" | OpenSSH配置文件文件名 | |
serial_settings | Dict | None | 串口连接的设置 | |
超时参数(单位为秒) | conn_timeout | int | 10 | 建立TCP连接的超时时间 |
auth_timeout | int | 10 | 设置等待认证响应的超时时间 | |
banner_timeout | int | 15 | 等待banner出现的超时时间 | |
blocking_timeout | int | 20 | 阻塞读写操作的超时时间,如果有值则会在读写channel阻塞超时后抛出异常 | |
timeout | int | 20 | 可以用来调整改变读取通道的max_loops | |
session_timeout | int | 60 | 设置并发请求的超时时间 | |
read_timeout_override | float | None | 用来覆盖读取超时的设置 | |
global_delay_factor | float | 1.0 | 影响读取通道间隔延迟的乘法因子(默认值:1) | |
delay_factor_compat | bool | False | 设置send_command和send_command_timing使用Netmiko3.x中delay_factor/global_delay_factor/max_loops的行为。这在Netmiko 5中被淘汰x(默认值:False) | |
keepalive | int | 0 | 按指定的时间间隔发送SSH keepalive报文,单位为秒 | |
与读写通道相关的参数 | global_cmd_verify | bool | None | 控制是否启用或禁用命令回显验证(默认值:没有)。全局属性优先于函数“cmd_verify”论点。“None”的值表示使用函数“cmd_verify”参数 |
default_enter | string | "" | 默认换行符(\n) | |
response_return | string | "" | 在规范化返回数据中要表示的字符舒付(默认:\n) | |
fast_cli | bool | True | 提供一种优化性能的方法。转换select_delay_factor选择最小的全局和特定。设置默认的global_delay_factor为0.1,这个值在Netmiko3.x中会影响到read_timeout的值 | |
_legacy_mode | bool | False | 逐行执行多条命令后,一起收集输出级过 | |
session_log | SessionLog | None | 用来写入会话日志的文件路径或BufferedIOBase子类对象。 | |
session_log_record_writes | bool | False | 会话日志通常只记录通道读操作,以消除命令回传造成的命令重复。如果你想在日志中都记录通道读和通道写,则需要将该值设为True | |
session_log_file_mode | string | write | session_log文件模式为write或append | |
allow_auto_change | bool | False | 允许自动更改终端宽度的设置,部分继承了基类的连接对象会用到该值。 | |
encoding | string | utf-8 | 向输出通道写入字节时使用的编码。 | |
sock | socket | None | 一个开放的套接字或类似套接字的对象(如' . channel '),用于与目标主机通信(默认:None)。 | |
auto_connect | bool | True | 是否在初始化连接对象后自动建立连接 |
通过上面表格的分类,大家应该都对连接对象的参数有了清晰的认识,虽然每个分类里的参数都比较多,但并不是都需要全部用到,我这里只把常用的几个参数通过高亮标识里出来。
参数调整
连接设备必须的基本参数和认证参数就不做赘述了,大家可以根据自己的实际情况选择是用户名/密码方式连接,还是密钥连接。
但超时参数里有几个参数是比较重要的。
- 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]]:
其实源码中的类,函数,参数的作用都和实际的命名相关,所以大家可以多去翻译一下,这样不仅可以在后续编程的过程中养成良好的命名习惯,也可以慢慢的面对陌生的函数或参数大概猜到其用法。
参数分类
类别 | 参数名 | 参数类型 | 默认值 | 含义 |
---|---|---|---|---|
基本参数 | command_string | string | "" | 在设备上执行的命令 |
expect_string | string | None | 用于判定输出结束的正则表达式/字符串,如果不传则默认自动获取设备标识符 | |
超时参数 | read_timeout | float | 10.0 | 循环读取通道的超时时间 |
delay_factor | float | None | 确定超时时间延迟乘数,只有delay_factor_compat设为True时才使用,通常在Netmiko4.x/5.x中不使用 | |
max_loops | int | None | 确定超时时间的最大循环次数,只有delay_factor_compat设为True时才使用,通常在Netmiko4.x/5.x中不使用 | |
格式化参数 | auto_find_prompt | bool | True | 自动获取设备标识 |
strip_command | bool | True | 删除执行命令的回显信息,通常为删除结果的首行并处理特殊字符 | |
strp_prompt | bool | True | 从输出中删除末尾的路由器提示符 | |
normalize | bool | True | 去除command_string的多余空格并末尾添加换行符 | |
cmd_verify | bool | True | 在获取命令结果之前,验证命令回显,确保不会读取到无用的通道buffer数据 | |
解析参数 | use_textfsm | bool | False | 通过TextFSM模板处理命令回显信息 |
textfsm_template | string | None | TextFSM模版的文件名 | |
use_ttp | bool | False | 通过TTP模板处理命令输出 | |
ttp_template | string | None | TTP模版的文件名 | |
use_genie | bool | False | 通过PyATS/Genie解析器处理命令输出 |
参数调整
执行的命令的参数中大多数都是默认设置,并且都设为了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_commands | 多种 | None | 要执行的配置命令 |
exit_config_mode | bool | True | 执行完配置命令之后是否退出配置模式 |
read_timeout | float | None | 如果不传该参数会默认设置为15s, |
config_mode_command | string | None | 进入配置模式的命令,各厂商会在自己的连接类里定义 |
cmd_verify | bool | True | 每条配置命令都验证是否输出命令echo |
enter_config_mode | bool | True | 是否进入配置模式,默认进入 |
error_pattern | string | "" | 输出结果出现匹配字符串时视为执行异常 |
terminator | string | r'#' | 配置模式下判定命令输出结束的正则字符串 |
bypass_commands | string | None | 与要执行的命令匹配,如果匹配成功则关闭cmd_verify功能 |
上述参数虽然比较多,但都较好理解,部分参数我也会在视频中更通俗易懂的讲解一下,但这个函数封装的比较完善,绝大多数情况都只需要传递config_command
即可,该参数的值可以是字符串,或数组。
总结
这一章节主要是对Netmiko中的核心函数的讲解,这些函数的参数也都给大家分类展示了出来,更容易理解和使用,但还是需要从实践中加深理解;而且Netmiko的封装过程都是基于网络设备的交互逻辑,所以大家也不需要死记硬背,只要结合网络运维的经验即可理解掌握。
下一章节我会给大家代入一个简单的巡检场景,讲解如何真正编写一个健壮的与网络设备交互的代码。
转载自:https://juejin.cn/post/7171270381947322398