Python 线程和 Telnet 意外结果

Python Threading and Telnet unexpected results

我编写了一个脚本,可以远程登录到某些网络设备并提取清单并将其放入文件中。我使用线程来加速脚本,因为我有大约 160 个节点。我写了一个函数,远程登录到每个节点,检索清单,将输出写入文件,然后断开会话。当我在一个函数下执行这三个步骤时,该脚本效果很好。但是,我决定创建一个 class 并将这三个步骤分解为不同的方法。现在当我 运行 脚本时,它只从列表中的第一个节点检索数据。我需要一些帮助来弄清楚为什么脚本不起作用。

import concurrent.futures
import time
import telnetlib
from myFunctions import get_node_list_TA5K

class Ta5kTelnet:

    def __init__(self):
        self.tn = telnetlib.Telnet()

    def connect(self, hostname, username, password):
        self.tn.open(hostname, 23, 5)
        self.tn.read_until(b'Username:', 5)
        self.tn.write(username.encode('ascii') + b'\n')
        self.tn.read_until(b'Password:', 5)
        self.tn.write(password.encode('ascii') + b'\n')
        if b'>' in self.tn.read_until(b'>', 5):
            self.tn.write(b'en\n')
            self.tn.read_until(b'#', 5)
            self.tn.write(b'term len 0\n')
            output = self.tn.read_until(b'#', 5)
            return output
        else:
            print(f'{hostname} is spare shelf')

    def send(self, command, waitfor):
        self.tn.write(command + b'\n')
        result = self.tn.read_until(waitfor, 180).decode()
        return result
    
    def disconnect(self):
        self.tn.close()


tlnt = Ta5kTelnet()


def inventory(node_names):
    tlnt.connect(node_names, 'username', 'password')
    shelf_inventory = tlnt.send(b'show system inventory', b'#')
    tlnt.disconnect()
    with open(f'{node_names}_inventory.txt', 'a') as f:
        f.write(shelf_inventory)



#adtran_nodes = get_node_list_TA5K()
adtran_nodes = ['BXR1-NODE1-COT1', 'BXR6-NODE1-COT6']


start = time.perf_counter()

with concurrent.futures.ThreadPoolExecutor() as executor:
    results = executor.map(inventory, adtran_nodes)



print(time.perf_counter() - start)

您正在跨两个线程共享单个 telnet 连接,并且没有锁定或同步。

没有人知道连接、登录、发送的顺序,read_until 序列将是 运行。

每个线程都需要自己的连接对象。

此外,我会重构从 telnetlib.Telnet 派生的东西,同时,为了确保连接始终正确关闭,使它成为具有 __enter____exit__:

import concurrent.futures
import telnetlib

USERNAME = "username"
PASSWORD = "password"


class Ta5kTelnet(telnetlib.Telnet):
    def __enter__(self):
        return self

    def __exit__(self, *args):
        self.close()

    def login(self, hostname, username, password):
        self.open(hostname, 23, 5)
        self.read_until(b"Username:", 5)
        self.write(username.encode("ascii") + b"\n")
        self.read_until(b"Password:", 5)
        self.write(password.encode("ascii") + b"\n")
        if b">" in self.read_until(b">", 5):
            self.write(b"en\n")
            self.read_until(b"#", 5)
            self.write(b"term len 0\n")
            return self.read_until(b"#", 5)
        raise RuntimeError(f"{hostname} is spare shelf")

    def send_command(self, command, waitfor):
        self.write(command + b"\n")
        return self.read_until(waitfor, 180).decode()


def get_node_inventory(node_name):
    with Ta5kTelnet() as tlnt:
        tlnt.login(node_name, USERNAME, PASSWORD)
        shelf_inventory = tlnt.send_command(b"show system inventory", b"#")
    with open(f"{node_name}_inventory.txt", "a") as f:
        f.write(shelf_inventory)


adtran_nodes = ["BXR1-NODE1-COT1", "BXR6-NODE1-COT6"]

with concurrent.futures.ThreadPoolExecutor() as executor:
    results = executor.map(get_node_inventory, adtran_nodes)