You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

修复Cisco路由器SSH连接问题并完善配置代码求助

Netmiko连接Cisco路由器超时及代码错误修复

错误现象

SSH连接成功后触发超时,报错信息如下:

INFO:paramiko.transport:Connected (version 1.99, client Cisco-1.25)
INFO:paramiko.transport:Authentication (password) successful!
INFO:root:Connection established
ERROR:root:An error occurred: Timed-out reading channel, data not available.
INFO:root:The connection has concluded

同时收到net_connect.disconect()未定义的提示。

问题分析与修复

原代码存在命令格式错误、连接管理不当、功能缺失等问题,以下是修复后的完整代码及关键说明:

修复后的完整代码

from netmiko import ConnectHandler
import getpass
import difflib
import logging

# 日志配置
logging.basicConfig(level=logging.INFO)

# 保存配置到本地文件
def save_config_to_file(config, filename):
    with open(filename, 'w') as config_file:
        config_file.write(config)
    logging.info(f'Configuration saved to {filename}')

# 对比并显示配置差异
def show_differences(config1_label, config1, config2_label, config2):
    difference = difflib.Differ()
    diff = list(difference.compare(config1.splitlines(), config2.splitlines()))

    for line in diff:
        if line.startswith('- '):
            logging.info(f'Present in {config1_label}: {line[2:]}')
        elif line.startswith('+ '):
            logging.info(f'Present in {config2_label}: {line[2:]}')

# 配置设备(含环回、物理接口、OSPF)
def configure_device(net_connect):
    # 进入特权模式
    net_connect.enable()
    
    # 配置命令列表(无需手动加换行符,Netmiko自动处理模式切换)
    config_commands = [
        # 环回接口配置
        'interface Loopback0',
        'ip address 192.168.57.101 255.255.255.0',
        'exit',
        # 物理接口0配置(启用接口)
        'interface GigabitEthernet0/0',
        'ip address 192.168.58.101 255.255.255.0',
        'no shutdown',
        'exit',
        # 物理接口1配置(启用接口)
        'interface GigabitEthernet0/1',
        'ip address 192.168.59.101 255.255.255.0',
        'no shutdown',
        'exit',
        # ACL配置(修正为Cisco反掩码)
        'ip access-list extended MY_ACL',
        'permit ip 192.168.56.130 0.0.0.255 any',
        'deny ip any any',
        'exit',
        # OSPF配置及网段宣告
        'router ospf 1',
        'router-id 192.168.57.101',
        'network 192.168.57.0 0.0.0.255 area 0',
        'network 192.168.58.0 0.0.0.255 area 0',
        'network 192.168.59.0 0.0.0.255 area 0',
        'exit'
    ]
    
    # 发送配置命令,自动进入全局配置模式
    output = net_connect.send_config_set(config_commands)
    print(output)
    
    # 保存配置到设备NVRAM
    net_connect.save_config()

# 主逻辑
def main():
    host = '192.168.56.101'
    username = input('Please enter your username: ')
    password = getpass.getpass('Please enter your password: ')
    secret = 'cisco'

    choice = input('Would you like to connect by using telnet or ssh? ')

    if choice.lower() == 'telnet':
        device_type = 'cisco_ios_telnet'
    elif choice.lower() == 'ssh':
        device_type = 'cisco_ios'
    else:
        raise ValueError('Invalid - Please choose either telnet or ssh: ')

    device = {
        'device_type': device_type,
        'host': host,
        'username': username,
        'password': password,
        'secret': secret,
        'port': 22 if device_type == 'cisco_ios' else 23,
        'timeout': 100,
        'fast_cli': False,  # 禁用快速CLI模式,提升Cisco设备兼容性
    }

    try:
        # with语句自动管理连接生命周期,无需手动断开
        with ConnectHandler(**device) as net_connect:
            logging.info('Connection established')

            # 执行设备配置
            configure_device(net_connect)

            # 获取并保存运行配置
            running_configuration = net_connect.send_command('show running-config')
            local_config_file_name = 'local_config.txt'
            save_config_to_file(running_configuration, local_config_file_name)
            logging.info(f'The configuration has been saved to {local_config_file_name}')

            # 对比本地与设备配置
            try:
                with open(local_config_file_name, 'r') as local_config_file:
                    local_config = local_config_file.read()

                if running_configuration and local_config:
                    if running_configuration == local_config:
                        logging.info('The running configuration is the same as the local configuration')
                    else:
                        logging.warning('The running configuration does not match the local configuration: ')
                        show_differences('Running Config', running_configuration, 'Local Config', local_config)
                else:
                    logging.error('Failed to access configurations')

            except FileNotFoundError:
                logging.error(f'The local configurations file, {local_config_file_name}, could not be found')

    except Exception as e:
        logging.error(f'An error occurred: {e}')

    logging.info('The connection has concluded')

if __name__ == "__main__":
    main()

关键修复点

  • 修正命令格式:移除配置命令中的\n及手动添加的en/conf t,send_config_set会自动进入全局配置模式,多余的换行符会导致设备响应异常引发超时。
  • 修复连接管理:原代码在with块外调用disconnect(),with语句会自动关闭连接,此时net_connect已失效;移除手动断开逻辑,依赖with自动管理。
  • 补充OSPF配置:添加OSPF进程号、router-id及正确的网段宣告(使用Cisco反掩码)。
  • 启用物理接口:物理接口默认处于shutdown状态,添加no shutdown命令启用接口。
  • 修正ACL掩码:Cisco ACL需使用反掩码,将255.255.255.0改为0.0.0.255。
  • 优化差异显示:原函数直接打印完整配置字符串,修改为传入标签,避免输出混乱。
  • 添加设备配置保存:调用save_config()将配置写入NVRAM,防止重启丢失。
  • 提升兼容性:添加fast_cli=False,禁用快速CLI模式,减少与Cisco设备的交互超时概率。

内容的提问来源于stack exchange,提问作者Louis16

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 23:13:11