如何判断远程ESXI主机是否完成启动并可接收SSH命令
问题根因
你遇到的循环意外退出问题,核心是两处代码疏漏导致异常没有被正确捕获,直接触发程序崩溃:
- 最后一个全局捕获块
except:没有绑定异常变量e,执行print(e)时会直接抛出NameError,直接终止整个函数执行,不会进入下一次重试。ESXI启动过程中抛出的Errno 111 Connection refused在你当前的异常列表里没有被完全覆盖,会掉到这个全局捕获块触发崩溃。 - 你代码中定义了总超时变量
timeout_start但没有实际做判断,存在死循环风险;同时每次重试没有主动清理SSH连接实例,可能产生连接句柄泄漏,触发ESXI侧SSH服务的临时连接限制,也会加重111错误的出现概率。
修复方案
修改后的代码如下,主要做了几处调整:
- 补充
ConnectionRefusedError显式捕获,覆盖111错误场景 - 修正全局异常捕获的变量绑定问题,避免
NameError - 每次重试都重新实例化SSHClient、主动关闭旧连接,避免连接状态污染
- 增加总超时逻辑,避免主机异常时无限重试
import time import socket import paramiko from socket import error as socket_error def send_command(ip, port, timeout, retry_interval, cmd, user, password): retry_interval = float(retry_interval) timeout = int(timeout) timeout_start = time.time() while time.time() < timeout_start + timeout: time.sleep(retry_interval) ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) try: ssh.connect(ip, port, user, password, timeout=5) stdin, stdout, stderr = ssh.exec_command(cmd) outlines = stdout.readlines() resp = ''.join(outlines) print(resp) ssh.close() return resp except ConnectionRefusedError as e: # 专门处理111连接拒绝,直接重试 print(f"Connection refused: {e}, retrying...") ssh.close() continue except socket_error as e: print(e) ssh.close() continue except paramiko.ssh_exception.SSHException as e: # socket是通的但SSH服务未就绪,重试 print(e) ssh.close() continue except TimeoutError as e: print(e) ssh.close() continue except socket.timeout as e: print(e) ssh.close() continue except paramiko.ssh_exception.NoValidConnectionsError as e: print(e) ssh.close() continue except socket.error as serr: print(serr) ssh.close() continue except IOError as e: print(e) ssh.close() continue except Exception as e: # 兜底捕获所有其他异常,确保不会退出循环 print(f"Unexpected error: {e}, retrying...") ssh.close() continue # 总超时退出 raise TimeoutError(f"Failed to connect to {ip} within {timeout} seconds")
补充说明
你提到的更换命令为esxcli system hostname get并校验返回字段的方案可以作为业务层面的就绪判断逻辑,避免SSH服务刚启动但ESXI系统层未就绪时返回无效结果,建议和异常捕获修复同时使用。
内容的提问来源于stack exchange,提问作者jasont41
相关产品推荐
相关产品推荐

