使用Pexpect通过SSH无法向Panduit PDU(Smartzone G5)下发命令的问题排查
Let's break down why your Pexpect script isn't triggering the port shutdown on your Panduit PDU, and fix it step by step.
1. Ditch the nested bash spawn (it's causing unnecessary complexity)
Right now you're spawning a bash shell first, then running SSH inside it. This adds extra layers that can mess with input routing and buffer handling. Instead, spawn the SSH command directly—this simplifies the session and eliminates bash-related interference:
child = pexpect.spawn(f'ssh -F /dev/null {server_user}@{server_ip} -oStrictHostKeyChecking=no') child.logfile_read = sys.stdout.buffer
2. Wait for the PDU's response (don't just sleep and exit)
Your current script sends the shutdown command but immediately terminates before the PDU can fully process it. Even with sleep(3), the script might close the session before the command takes effect. Replace the sleep with explicit expect() calls to wait for the PDU's confirmation or return to the prompt:
# After sending the port shutdown command child.sendline('dev outlet 1 1 off') # Wait for either the SUCCESS message or the prompt (fallback) try: index = child.expect(['SUCCESS', 'PANDUIT>'], timeout=10) if index == 0: print("\nPort 1 confirmed shut down!") else: print("\nCommand sent, returned to prompt.") except pexpect.TIMEOUT: print("\nTimeout: PDU didn't respond in time.") except pexpect.EOF: print("\nConnection closed unexpectedly.")
This ensures the script stays alive until the PDU acknowledges the command.
3. Fix your initial expect logic
That child.expect('') line in your original code doesn't serve any purpose. When spawning SSH directly, you can immediately wait for the password prompt:
child.expect(f"{server_user}@{server_ip}'s password:") child.sendline(server_pass) # Wait for the PDU's command prompt to confirm login child.expect('PANDUIT>')
4. Add terminal emulation tweaks (some PDUs are picky)
Many network devices require a proper pseudo-terminal or specific terminal type to accept interactive commands. Add the -t flag to force terminal allocation and set a basic terminal type:
child = pexpect.spawn(f'ssh -F /dev/null -t {server_user}@{server_ip} -oStrictHostKeyChecking=no -oTerm=xterm')
Full revised script
Putting all these fixes together, here's the updated code:
import pexpect import sys server_ip = "192.168.0.1" server_user = "admin" server_pass = "password" # Spawn SSH directly with terminal tweaks child = pexpect.spawn(f'ssh -F /dev/null -t {server_user}@{server_ip} -oStrictHostKeyChecking=no -oTerm=xterm') child.logfile_read = sys.stdout.buffer # Handle login password prompt child.expect(f"{server_user}@{server_ip}'s password:") child.sendline(server_pass) # Wait for the PDU command prompt to confirm successful login child.expect('PANDUIT>') # Send the port shutdown command child.sendline('dev outlet 1 1 off') # Wait for confirmation or prompt return try: index = child.expect(['SUCCESS', 'PANDUIT>'], timeout=10) if index == 0: print("\nPort 1 successfully turned off!") else: print("\nCommand executed, returned to prompt.") except pexpect.TIMEOUT: print("\nTimeout: PDU did not respond in time.") except pexpect.EOF: print("\nConnection closed unexpectedly.")
Why this works better:
- Direct SSH spawn eliminates bash buffer routing issues
- Explicit response waiting ensures the command is fully processed
- Terminal settings fix compatibility with picky network devices
- Error handling for timeouts/EOF makes debugging easier
If you still can't capture the SUCCESS message, double-check if the PDU outputs it with trailing whitespace or special characters—you might need to adjust the expect pattern to something like r'SUCCESS\r\n' to match exactly.
内容的提问来源于stack exchange,提问作者David O'Hara

