试图使 asyncio 与 telnetlib 一起工作

Posted

技术标签:

【中文标题】试图使 asyncio 与 telnetlib 一起工作【英文标题】:Trying to make asyncio working with telnetlib 【发布时间】:2021-02-24 18:35:46 【问题描述】:

我很难让 asyncio 与 telnetlib 一起工作以询问某些硬件。

我想我显然不明白 asyncio 的工作方式,我完全迷失在这一切中。真的不清楚。

我的基本版本(同步)运行良好,但查询完整的设备列表实际上需要 6 个小时,并且大部分设备没有响应,因为它们无法访问。

由于 Asyncio 使我们能够并行化连接而无需等待每次超时触发,我想将我的代码转换为适当的异步代码,但没有成功。

这是我尝试过的:

import telnetlib
import time
import datetime
import asyncio

from env.fonctions import *
from env.variables import *


first_cmds = ['term length 0', \
              'show run', \
              'exit']

#create lists to iterate through
hosts = ['router-1', 'router-2', 'router-3', 'router-4', 'router-5']

async def main(hosts, user_rw_hw ,password_rw_hw, first_cmds):
    class ContinueI(Exception):
        pass

    continue_i = ContinueI()

    for host in hosts:
        print(f'host | Trying to connect...')
        try:
            tn = await async_establish_telnet_connexion(user_rw_hw ,password_rw_hw, host, 23, 0.5, True)
        except:
            continue

        print(f'host | Checking if equipment is not Nexus')

        tn.write('show version'.encode('ascii') + b"\n")
        sh_ver = await async_read_telnet_output(tn)
        if 'Nexus' in sh_ver or 'NX-OS' in sh_ver or 'nexus' in sh_ver:
            print(f'host | Equipment is Nexus, closing connection...')
            tn.write('exit'.encode('ascii') + b"\n")
            continue

        tn.write(''.encode('ascii') + b"\n")

        try:
            for cmd in first_cmds:
                tn.write(cmd.encode('ascii') + b"\n")
                if not 'exit' in cmd:
                    response = await async_read_telnet_output(tn)
                    if '\r\n% Invalid' in response:
                        print(f'host | Commande "cmd" pas reconnue')
                        raise continue_i
                else:
                    print(f'host | Commands are accepted')
        except ContinueI:
            tn.write(b"exit\n")
            tn.write(b"exit\n")
            print(f'host | Logout for command not recognized')
            continue


if __name__ == "__main__":
    try:
        loop = asyncio.get_event_loop()
        loop.set_debug(1)
        loop.run_until_complete(main(hosts, user_rw_hw ,password_rw_hw, first_cmds))

    except Exception as e:
        pass

    finally:
        loop.close()

和功能:

async def async_read_telnet_output(tn, timeout=2, timestep=0.1):
   timer = 0
   data = b''
   while timer <= timeout:
       new_datas = tn.read_very_eager()
       if len(new_datas) != 0:
           timer = 0
           data += new_datas
       await asyncio.wait(timestep)
       timer += timestep
   return data.decode('utf-8')


async def async_establish_telnet_connexion(user_rw_hw, password_rw_hw, host, port=23, timeout=1, debug=False):
   try:
       tn = telnetlib.Telnet(host, port)    # Here I don't know how to make it awaitable, if I put await before the IDE said that this method is not an awaitable, btw even if I put an awaitable like "asyncio.sleep" the behavior is still the same so it's not the only point bad
   except:
       if debug == True:
           print(f"host | Telnet not responding.")
       raise Exception

   if debug == True:
       print(f"host | Telnet is responding.")

   response = loop.create_task(async_read_telnet_output(tn, 15))

   if not 'Username:' in response and not 'login' in response:
       if debug == True:
           print(f"host | Don't see Username asked by equipment.")
       raise Exception
   else:
       tn.write(user_rw_hw.encode('ascii') + b"\n")

   if debug == True:
       print(f"host | Username entered.")

   try:
       await tn.read_until(b"Password: ", timeout)
   except:
       if debug == True:
           print(f"host | Don't see Password asked by equipment.")
       raise Exception
   finally:
       tn.write(password_rw_hw.encode('ascii') + b"\n")
       response = await async_read_telnet_output(tn, 10)
       if '% Authentication failed' in response or 'Rejected' in response:
           if debug == True:
               print(f"host | Connection failed bad credentials.")
           raise Exception
       if debug == True:
           print(f"host | Connection succeed waiting for commands.")
       return tn

如果有人知道我失败的地方,我会很感激我被困了一个星期......阅读一些书籍和 youtube tutos 但没有任何帮助......

提前谢谢你!

【问题讨论】:

虽然它实际上并不能解决您的问题,但您可能需要阅读How does asyncio actually work?。代码必须异步的,你不能仅仅await 一个同步函数并期望它变成并发的。使用线程而不是协程可能更容易解决您的问题。 好的,我马上看,谢谢MisterMiyagi :) 我现在明白你的意思@MisterMiyagi,我在想,由于 telnet 有时正在等待答案,它应该自动“异步”,但现在我明白你的意思了,我会仔细看看这点。事实上,我认为这是可能的,因为我的函数“async_read_telnet_output”使用的是 asyncio.sleep 方法,如果我理解得很好,那么我会看到重构我的代码,如果是不兼容的 telnetlib,我可以看看套接字库是否是“异步的”并从头开始创建一个新的 telnet 客户端。再次感谢! 【参考方案1】:

对于那些登陆这里的人......我找到了 https://pypi.org/project/asynctelnet/

快速示例:

客户:

import anyio, asynctelnet

async def shell(tcp):
    async with asynctelnet.TelnetClient(tcp, client=True) as stream:
        while True:
            # read stream until '?' mark is found
            outp = await stream.receive(1024)
            if not outp:
                # End of File
                break
            elif '?' in outp:
                # reply all questions with 'y'.
                await stream.send('y')

            # display all server output
            print(outp, flush=True)

    # EOF
    print()

async def main():
    async with await connect_tcp('localhost', 56023) as client:
        await shell(client)
anyio.run(main)

服务器:

import anyio, asynctelnet

async def shell(tcp):
    async with asynctelnet.TelnetServer(tcp) as stream:
        # this will fail if no charset has been negotiated
        await stream.send('\r\nWould you like to play a game? ')
        inp = await reader.receive(1)
        if inp:
            await stream.echo(inp)
            await stream.send('\r\nThey say the only way to win '
                              'is to not play at all.\r\n')

async def main():
    listener = await anyio.create_tcp_listener(local_port=56023)
    await listener.serve(shell)
anyio.run(main)

这个库仍然有一些错误。所以目前的状态是“如果你想使用它,请准备好编写一些错误解决方法”。

【讨论】:

以上是关于试图使 asyncio 与 telnetlib 一起工作的主要内容,如果未能解决你的问题,请参考以下文章

python asyncio运行一次事件循环?

Python3+telnetlib实现telnet客户端

concurrent.futures 和 asyncio.futures 有啥区别?

Python asyncio 与 Slack 机器人

python asyncio中的不和谐机器人

python协程asyncio:async与await