通过从 python 向 SSH CLI 发送命令来控制 TP-Link 交换机

问题描述 投票:0回答:1

我想做的事情

你好。最近我遇到了控制 TP-Link TL-SG2218 的问题。它在供应商固件上嵌入了 SSH CLI。使用标准控制台 linux 控制台 SSH 客户端 CLI 工作起来非常完美。这是我正在做的示例(我正在启用端口 10)

unit-1@unit1:~$ ssh [email protected]
[email protected]'s password: 
TL-SG3428>enable
TL-SG3428#configure
TL-SG3428(config)#interface gigabitEthernet 1/0/10
TL-SG3428(config-if)#no shutdown
TL-SG3428(config-if)#exit
TL-SG3428(config)#exit
TL-SG3428#copy running-config startup-config 
 Start to save user config as the Next Startup Config file......
 Saving user config OK!
TL-SG3428#exit
TL-SG3428>exit
Connection to 192.168.21.79 closed.
unit-1@unit1:~$ 

所以,工作真的很快而且完美

我需要做同样的事情,但是来自Python代码 我尝试过的:

解决方案1:

使用标准 SSH 库(paramiko)

ssh = paramiko.SSHClient()
ssh.connect(ip, username=login, password=password, auth_timeout=20,
                             channel_timeout=20)
ssh.exec_command(command)

使用标准 exec_command 时出现 EOF 错误方法

解决方案2:

使用paramiko,但实现交互式shell(使用通道)

ssh = paramiko.SSHClient()
ssh.connect(ip, username=login, password=password, auth_timeout=20,
                             channel_timeout=20)
channel = ssh.invoke_shell()
self.channel.send(command.encode("UTF-8"))
buff = ''
while "#" not in buff and ">" not in buff:
    resp = self.channel.recv(4096)
    buff += resp.decode("UTF-8")
    print(buff)
print(buff)

陷入无限循环 如果我删除 while 循环来读取响应,并且只是通过小停顿传递命令,那么这是行不通的

解决方案3(Netmiko)

我找到了这个Python库:netmiko 使用这段代码我终于能够正确执行命令

from netmiko import ConnectHandler
switch = {
    'device_type': 'tplink_jetstream',
    'host': ip,
    'username': login,
    'password': password
}
net_connect = ConnectHandler(**switch)
net_connect.send_command(command) 

但我发现,当我运行完整场景时,netmiko 在大约 50% 的情况下工作 在另外 50% 的情况下,我总是会遇到这个异常(绝对随机,在随机命令之后)

[EXECUTING]enable
Already in admin level.



[EXECUTING]configure



TL-SG3428#configure


[EXECUTING]interface gigabitEthernet 1/0/10
INFO:     172.22.0.1:55758 - "POST /disable_switch HTTP/1.1" 500 Internal Server Error
ERROR:    Exception in ASGI application
Traceback (most recent call last):
  File "/usr/local/lib/python3.11/site-packages/uvicorn/protocols/http/httptools_impl.py", line 401, in run_asgi
    result = await app(  # type: ignore[func-returns-value]
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/uvicorn/middleware/proxy_headers.py", line 70, in __call__
    return await self.app(scope, receive, send)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/fastapi/applications.py", line 1054, in __call__
    await super().__call__(scope, receive, send)
  File "/usr/local/lib/python3.11/site-packages/starlette/applications.py", line 123, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/usr/local/lib/python3.11/site-packages/starlette/middleware/errors.py", line 186, in __call__
    raise exc
  File "/usr/local/lib/python3.11/site-packages/starlette/middleware/errors.py", line 164, in __call__
    await self.app(scope, receive, _send)
  File "/usr/local/lib/python3.11/site-packages/starlette/middleware/exceptions.py", line 65, in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
  File "/usr/local/lib/python3.11/site-packages/starlette/_exception_handler.py", line 64, in wrapped_app
    raise exc
  File "/usr/local/lib/python3.11/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
    await app(scope, receive, sender)
  File "/usr/local/lib/python3.11/site-packages/starlette/routing.py", line 756, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/usr/local/lib/python3.11/site-packages/starlette/routing.py", line 776, in app
    await route.handle(scope, receive, send)
  File "/usr/local/lib/python3.11/site-packages/starlette/routing.py", line 297, in handle
    await self.app(scope, receive, send)
  File "/usr/local/lib/python3.11/site-packages/starlette/routing.py", line 77, in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
  File "/usr/local/lib/python3.11/site-packages/starlette/_exception_handler.py", line 64, in wrapped_app
    raise exc
  File "/usr/local/lib/python3.11/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
    await app(scope, receive, sender)
  File "/usr/local/lib/python3.11/site-packages/starlette/routing.py", line 72, in app
    response = await func(request)
               ^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/contextlib.py", line 222, in __aexit__
    await self.gen.athrow(typ, value, traceback)
  File "/usr/local/lib/python3.11/site-packages/fastapi/concurrency.py", line 35, in contextmanager_in_threadpool
    raise e
netmiko.exceptions.ReadTimeout: 
Pattern not detected: 'TL\\-SG3428\\(config\\)\\#' in output.

Things you might try to fix this:
1. Explicitly set your pattern using the expect_string argument.
2. Increase the read_timeout to a larger value.

You can also look at the Netmiko session_log or debug log for more information.

我真的快没精神了 你有什么建议? 我想到两个办法

  1. 使用 python 中的控制台 linux ssh 客户端,比如 Popen 等,并与之通信?
  2. 也许可以微调netmiko中的expect_string模式? (因为它基本上有效,只是并不总是有效)
  3. 也许还有其他什么?我只需要简单的方法来运行 CLI 命令并获取结果,就像在 Linux SSH 客户端中那样?
python ssh command-line-interface router
1个回答
0
投票

尝试使用 Pexpect Lib(

Pexpect 是一个 Python 模块,允许生成子应用程序,然后进行控制,并响应输出中的预期模式。

import pexpect

# Start the SSH connection to the switch
child = pexpect.spawn('ssh [email protected]')

# Wait for the password prompt and send the password
child.expect("password:")
child.sendline('your_password_here')  # Replace 'your_password_here' with the correct password

# Wait for the switch prompt
child.expect('TL-SG3428>')

# Send the 'enable' command
child.sendline('enable')

# Wait for the privileged mode prompt
child.expect('TL-SG3428#')

# Send the 'configure' command
child.sendline('configure')

# Wait for the global configuration prompt
child.expect('TL-SG3428\(config\)#')

# Send the command to enter the specified interface
child.sendline('interface gigabitEthernet 1/0/10')

# Wait for the interface configuration prompt
child.expect('TL-SG3428\(config-if\)#')

# Send the 'no shutdown' command
child.sendline('no shutdown')

# Exit interface configuration mode
child.sendline('exit')

# Wait for the global configuration prompt
child.expect('TL-SG3428\(config\)#')

# Exit global configuration mode
child.sendline('exit')

# Wait for the privileged mode prompt
child.expect('TL-SG3428#')

# Send the command to save the configuration
child.sendline('copy running-config startup-config')

# Wait for the save confirmation message
child.expect('Saving user config OK!')

# Exit privileged mode
child.sendline('exit')

# Exit EXEC mode
child.expect('TL-SG3428>')

# Close the SSH session
child.sendline('exit')

# Wait for the connection to close
child.expect(pexpect.EOF)

# Print the complete session output
print(child.before.decode('utf-8'))
© www.soinside.com 2019 - 2024. All rights reserved.