Netmiko 脚本未连接到多个主机

Netmiko Script not connecting to multiple hosts

我正在编写一个脚本,该脚本从 CSV 文件中提取数据并连接到多个主机以执行 运行 命令。该脚本的行为就像连接到所有不同的设备,但实际上它只连接到列表中的第一个设备。我需要更改什么才能使其正常工作?

示例 CSV 文件:

hostnames,device_platform,device_role
co-acc-v1.nunya.com,cisco_ios,co-acc-v
co-agg-r1.nunya.com,cisco_ios,co-agg-r
co-edg-fw1.nunya.com,cisco_asa,co-edg-fw
co-acc-sw1.nunya.com,cisco_ios,co-acc-sw
co-acc-rsw1.nunya.com,broadcom_icos,co-acc-rsw

脚本如下:

from netmiko import ConnectHandler
import concurrent.futures
import csv
import datetime
import time

username = 'Cisco'
pwd = 'Cisco123'
t1 = time.perf_counter()


def fetch_hostnames():
    with open('devices.csv', 'r') as file:
        reader = csv.reader(file)
        keys = next(reader)[1:]
        hostnames = {key: dict(zip(keys, values)) for key, *values in reader}
    return hostnames


def verification_file(filename, output, hostname):
    with open(filename, 'w') as output_file:
        output_file.write(output)
        print(f'Verification commands were successfully captured on {hostname}!')
    return


def run_verification_commands(hostname):
    today_date = datetime.datetime.now()
    year = today_date.year
    day = today_date.day
    month = today_date.month

    connection_info = {
        'port': 22,
        'username': username.lower(),
        'password': pwd,
        'secret': pwd,
        'fast_cli': False
    }

    for device, info in fetch_hostnames().items():
        platform = info['device_platform']
        print(f'Connecting to host {hostname}...')
        ssh_connection = ConnectHandler(ip=device, device_type=platform, banner_timeout=200, **connection_info)
        ssh_connection.enable()
        ssh_connection.send_command('terminal length 0', strip_prompt=False, strip_command=False)
        print(f'Generating running configuration for host {hostname}...')
        output = ssh_connection.send_command('show running-config', strip_prompt=False, strip_command=False)
        prompt_hostname = ssh_connection.find_prompt()[0:-1]
        filename = f'{prompt_hostname}_{month}_{day}_{year}_verification.txt'

        print(f'Backing up configuration for host {hostname}')
        time.sleep(1)
        verification_file(filename, output, hostname)
        ssh_connection.disconnect()
        return


with concurrent.futures.ThreadPoolExecutor() as exe:
    hosts = fetch_hostnames()
    results = exe.map(run_verification_commands, hosts)

t2 = time.perf_counter()
print(f'The script finished executing in {round(t2-t1,2)} seconds.')

这是脚本的输出:

"C:\Program Files\Python39\python.exe" "C:/Scripts/Python/NetChecks/NC/test.py"
Connecting to host co-acc-v1.nunya.com...
Connecting to host co-agg-r1.nunya.com...
Connecting to host co-edg-fw1.nunya.com...
Connecting to host co-acc-sw1.nunya.com...
Connecting to host co-acc-rsw1.nunya.com...
Generating running configuration for host co-acc-v1.nunya.com...
Generating running configuration for host co-agg-r1.nunya.com...
Generating running configuration for host co-edg-fw1.nunya.com...
Generating running configuration for host co-acc-sw1.nunya.com...
Generating running configuration for host co-acc-rsw1.nunya.com...
Backing up configuration for host co-acc-v1.nunya.com...
Backing up configuration for host co-agg-r1.nunya.com...
Backing up configuration for host co-edg-fw1.nunya.com...
Backing up configuration for host co-acc-sw1.nunya.com...
Backing up configuration for host co-acc-rsw1.nunya.com...
Verification commands were successfully captured on co-acc-v1.nunya.com!
Verification commands were successfully captured on co-agg-r1.nunya.com!
Verification commands were successfully captured on co-edg-fw1.nunya.com!
Verification commands were successfully captured on co-acc-sw1.nunya.com!
Verification commands were successfully captured on co-acc-rsw1.nunya.com!

但是当脚本 运行s 并且如果我在 co-acc-v1.nunya.com 上执行 show users 它显示我已连接多次:

co-acc-v1#show users
    Line       User       Host(s)              Idle       Location
*  2 vty 0     cisco       idle               00:00:01     1.1.1.1
   3 vty 1     cisco       idle               00:00:02     1.1.1.1
   4 vty 2     cisco       idle               00:00:00     1.1.1.1
   5 vty 3     cisco       idle               00:00:00     1.1.1.1
   6 vty 4     cisco       idle               00:00:01     1.1.1.1
   7 vty 5     cisco       idle               00:00:01     1.1.1.1

  Interface    User               Mode         Idle     Peer Address

co-acc-v1#

for device, info in fetch_hostnames().items():

这个 for 循环是不必要的,因为它已经对每个主机执行了操作:

results = exe.map(run_verification_commands, hosts)

每次调用run_verification_commands时,无论传递给它什么,因为for循环与原始数据相反,它总是会抓住第一个主机。

这可以通过在设置并发期货时传入数据来解决:

with concurrent.futures.ThreadPoolExecutor() as exe:
    hostinfos = fetch_hostnames().items()
    results = exe.map(run_verification_commands, hostinfos)

hostinfos 在这种情况下是一个元组列表,元组包括:

(<hostname>, <info>)

然后run_verification_commands可以把主机名和信息分开:

def run_verification_commands(hostinfo):
    hostname, info = hostinfo

现在可以去掉这一行,里面的代码被拉回一级:

for device, info in fetch_hostnames().items():

并修改了这一行:

ssh_connection = ConnectHandler(ip=device ...ssh_connection = ConnectHandler(ip=hostname ...

这将使它像您希望的那样循环。

最后一个音符:

open(filename, 'w')

每次都会覆盖文件。如果要追加,请将 w(写入)更改为 a(追加)