<think>我们之前已经给出了一个完整的并行SSH连接检查方案,现在用户的问题聚焦在如何通过SSH并行连接多个虚拟机并检查它们是否已成功启动。注意,这里的关键是“检查虚拟机是否启动成功”,而不仅仅是SSH连接。因此,我们需要考虑:
1. 虚拟机启动过程中SSH服务可能还未完全启动,所以需要重试机制。
2. 除了SSH连接,我们还可以通过执行一些命令来检查系统状态(如检查系统运行时间、关键服务状态等)。
结合引用[2]中提到的通过Shell脚本自动化创建虚拟机,我们可以将启动检查与自动化创建流程结合。
修改方案:在原有并行SSH连接的基础上,增加重试机制和启动状态检查。
步骤:
1. 尝试SSH连接,如果连接成功,则执行一个命令(如查看系统运行时间)来确认系统已经启动一段时间(避免刚启动瞬间的连接成功但系统还不稳定)。
2. 如果连接失败,则等待一段时间后重试,直到达到最大重试次数。
我们设计一个函数,它会在一定时间内重试连接,并执行一个简单的命令来验证系统是否已启动完成。
注意:由于虚拟机启动需要时间,我们不应该在启动后立即连接,而是等待一段时间(比如30秒)再开始尝试。但为了通用性,我们让调用者决定何时开始检查(即调用函数时,虚拟机应该已经启动了一段时间)。
但是,如果我们要在创建虚拟机后立即检查,那么我们需要在代码中内置重试逻辑。
改进后的方案:
我们将创建一个新的函数,该函数将循环尝试连接,直到成功或超过最大尝试次数。同时,我们使用异步并行执行多个虚拟机的检查。
具体实现:
1. 定义一个异步函数,用于检查单个虚拟机的启动状态(包括重试)。
2. 在检查启动状态时,不仅连接SSH,还要执行一个命令(如`uptime`)来确认系统已经启动并且可以执行命令。
3. 并行执行所有虚拟机的检查。
由于我们不知道虚拟机启动需要多长时间,所以设置:
- 重试次数:比如10次
- 每次重试间隔:5秒
这样,最长等待时间为50秒(10次*5秒),通常足够虚拟机启动。
代码调整:
我们将创建一个新函数`check_vm_boot_status`,它包装了原来的`check_ssh_connection`,并添加重试逻辑。
注意:为了避免无限等待,我们使用超时和重试次数限制。
同时,我们也可以考虑使用指数退避策略(每次等待时间递增)来优化重试。
但是,为了简单,我们使用固定间隔重试。
代码实现:
```python
import asyncio
import asyncssh
from typing import List, Dict, Tuple, Optional
async def check_ssh_connection(host: str, username: str, password: str) -> Tuple[bool, str]:
"""检查单个虚拟机的SSH连接状态,并执行简单命令验证"""
try:
async with asyncssh.connect(
host=host,
username=username,
password=password,
known_hosts=None, # 跳过已知主机检查
connect_timeout=10, # 连接超时时间
login_timeout=5, # 登录超时时间
keepalive_interval=15 # 保持连接活跃
) as conn:
# 执行命令验证连接,同时检查系统启动状态(这里用uptime,但也可以使用其他命令)
result = await conn.run("uptime", timeout=5)
if result.exit_status == 0:
return True, f"{host}: Connection successful and system is up (uptime: {result.stdout.strip()})"
return False, f"{host}: Command execution failed"
except (asyncssh.ConnectionLost, OSError) as e:
return False, f"{host}: Connection lost - {str(e)}"
except asyncssh.TimeoutError:
return False, f"{host}: Connection timeout"
except asyncssh.PermissionDenied:
return False, f"{host}: Authentication failed"
except Exception as e:
return False, f"{host}: Unexpected error - {str(e)}"
async def check_vm_boot_status(host: str, username: str, password: str, max_retries: int = 10, retry_interval: int = 5) -> Tuple[bool, str]:
"""
检查虚拟机启动状态,带重试机制
:param host: 虚拟机IP
:param username: 用户名
:param password: 密码
:param max_retries: 最大重试次数
:param retry_interval: 重试间隔(秒)
:return: (是否成功, 消息)
"""
for attempt in range(max_retries):
success, message = await check_ssh_connection(host, username, password)
if success:
return True, message
else:
# 如果不是最后一次尝试,则等待后重试
if attempt < max_retries - 1:
await asyncio.sleep(retry_interval)
return False, f"{host}: Failed to connect after {max_retries} attempts"
async def check_all_vms_boot_status(vm_list: List[Dict[str, str]]) -> Dict[str, Tuple[bool, str]]:
"""
并行检查所有虚拟机的启动状态
:param vm_list: 虚拟机配置列表
:return: 字典,键为host,值为(状态, 消息)
"""
tasks = []
for vm in vm_list:
task = asyncio.create_task(
check_vm_boot_status(vm['host'], vm['username'], vm['password'])
)
tasks.append(task)
results = await asyncio.gather(*tasks)
return {vm['host']: result for vm, result in zip(vm_list, results)}
# 使用示例
if __name__ == "__main__":
# 虚拟机配置列表
virtual_machines = [
{"host": "192.168.1.101", "username": "admin", "password": "pass101"},
{"host": "192.168.1.102", "username": "admin", "password": "pass102"},
{"host": "192.168.1.103", "username": "admin", "password": "pass103"},
{"host": "192.168.1.104", "username": "admin", "password": "pass104"},
]
print("开始检查虚拟机启动状态...")
results = asyncio.run(check_all_vms_boot_status(virtual_machines))
print("\n虚拟机启动状态检查结果:")
print("=" * 70)
for host, (status, message) in results.items():
status_str = "✓ 启动成功" if status else "✗ 启动失败"
print(f"{host:<15} | {status_str:<10} | {message}")
print("=" * 70)
# 统计
success_count = sum(1 for status, _ in results.values() if status)
print(f"\n启动成功: {success_count}/{len(virtual_machines)}")
print(f"启动失败: {len(virtual_machines) - success_count}/{len(virtual_machines)}")
```
这个方案在原有基础上增加了重试机制,确保在虚拟机启动过程中不断尝试连接,直到成功或超过最大重试次数。
注意:重试间隔和次数可以根据实际情况调整。如果虚拟机启动时间较长,可以增加重试次数或延长间隔。
另外,我们通过执行`uptime`命令来获取系统运行时间,这可以间接反映系统启动时间(运行时间很短则说明刚刚启动)。如果需要更精确的启动状态检查,可以检查系统启动日志(如`journalctl -b`)或`/var/run/reboot-required`等文件。但一般情况下,能够执行命令且返回成功,就认为启动成功。
引用[4]中提到了Xshell的高效运维,我们的脚本同样可以集成到自动化运维流程中,作为虚拟机启动后的健康检查步骤。
关于引用[1]和[3]提到的动态端口转发和隧道技术,虽然与当前问题无直接关系,但我们可以考虑在需要访问虚拟机内部服务时,通过SSH隧道安全地访问(比如数据库)。这可以作为后续扩展功能。
最后,我们生成相关问题:
§§相关问题§§
1. 如何调整SSH连接重试策略以优化启动检查?
2. 除了SSH连接,还有哪些方法可以检查虚拟机启动状态?
3. 如何通过SSH隧道访问虚拟机内部服务(如数据库)?</think># 通过SSH并行连接多个虚拟机并验证启动状态
我将为您提供一个完整的解决方案,通过SSH并行连接多个虚拟机并验证它们是否成功启动。这个方案结合了引用中的技术要点,并优化了并行处理和状态检查机制。
## 完整解决方案
```python
import asyncio
import asyncssh
from typing import List, Dict, Tuple
import time
async def check_vm_boot_status(
host: str,
username: str,
password: str,
max_retries: int = 5,
retry_interval: int = 10
) -> Tuple[bool, str]:
"""
检查虚拟机启动状态,包含重试机制
:param host: 虚拟机IP地址
:param username: SSH用户名
:param password: SSH密码
:param max_retries: 最大重试次数
:param retry_interval: 重试间隔(秒)
:return: (启动是否成功, 状态消息)
"""
for attempt in range(1, max_retries + 1):
try:
start_time = time.time()
async with asyncssh.connect(
host=host,
username=username,
password=password,
known_hosts=None,
connect_timeout=10,
login_timeout=5
) as conn:
# 检查关键系统服务状态
result = await conn.run("systemctl is-active multi-user.target", timeout=5)
latency = time.time() - start_time
if result.exit_status == 0 and "active" in result.stdout:
# 执行uptime命令获取启动时间
uptime_result = await conn.run("uptime -p", timeout=3)
uptime = uptime_result.stdout.strip() if uptime_result.exit_status == 0 else "N/A"
return True, f"启动成功 | 延迟: {latency:.2f}s | 运行时间: {uptime}"
return False, f"系统未完全启动 (尝试 {attempt}/{max_retries})"
except (asyncssh.ConnectionLost, OSError) as e:
await asyncio.sleep(retry_interval)
except asyncssh.TimeoutError:
await asyncio.sleep(retry_interval)
except asyncssh.PermissionDenied:
return False, "认证失败: 用户名或密码错误"
except Exception as e:
return False, f"意外错误: {str(e)}"
return False, f"连接失败: 超过最大重试次数({max_retries})"
async def parallel_vm_boot_check(
vm_list: List[Dict[str, str]],
max_parallel: int = 10
) -> Dict[str, Tuple[bool, str]]:
"""
并行检查所有虚拟机的启动状态
:param vm_list: 虚拟机配置列表
:param max_parallel: 最大并行连接数
:return: 结果字典 {host: (状态, 消息)}
"""
# 使用信号量控制并行度
semaphore = asyncio.Semaphore(max_parallel)
async def check_with_semaphore(vm):
async with semaphore:
return vm['host'], await check_vm_boot_status(
vm['host'], vm['username'], vm['password']
)
tasks = [check_with_semaphore(vm) for vm in vm_list]
results = await asyncio.gather(*tasks)
return {host: result for host, result in results}
def generate_vm_config(prefix: str, count: int, base_ip: str) -> List[Dict[str, str]]:
"""
生成虚拟机配置 (引用[2]中的自动化思路)
:param prefix: 主机名前缀
:param count: 虚拟机数量
:param base_ip: 基础IP地址 (如 "192.168.1.")
:return: 虚拟机配置列表
"""
return [
{
"host": f"{base_ip}{100 + i}", # 如 192.168.1.101
"username": "admin",
"password": f"pass{100 + i}",
"hostname": f"{prefix}-{100 + i}" # 如 ubuntu-101 (引用[2])
}
for i in range(count)
]
# 使用示例
if __name__ == "__main__":
# 生成虚拟机配置 (引用[2]中的自动化思路)
vm_configs = generate_vm_config("ubuntu", 5, "192.168.1.")
print("开始并行检查虚拟机启动状态...")
start_time = time.time()
results = asyncio.run(parallel_vm_boot_check(vm_configs))
total_time = time.time() - start_time
# 打印结果
print("\n虚拟机启动状态检查结果:")
print("=" * 80)
print(f"{'主机名/IP':<25} | {'状态':<10} | 详细信息")
print("-" * 80)
success_count = 0
for host, (status, message) in results.items():
hostname = next((vm['hostname'] for vm in vm_configs if vm['host'] == host), host)
status_str = "✓ 成功" if status else "✗ 失败"
print(f"{hostname} ({host}) | {status_str:<10} | {message}")
if status:
success_count += 1
print("=" * 80)
print(f"检查完成 | 总耗时: {total_time:.2f}秒")
print(f"成功: {success_count}/{len(vm_configs)} | 失败: {len(vm_configs) - success_count}/{len(vm_configs)}")
# 保存结果到文件
with open("vm_boot_report.txt", "w") as f:
f.write("虚拟机启动状态报告\n")
f.write("=" * 50 + "\n")
for host, (status, msg) in results.items():
f.write(f"{host}: {'成功' if status else '失败'} - {msg}\n")
f.write("\n总结: ")
f.write(f"成功 {success_count}/{len(vm_configs)} | 失败 {len(vm_configs) - success_count}/{len(vm_configs)}")
print("结果已保存到 vm_boot_report.txt")
```
## 关键特性说明
1. **并行连接管理**:
- 使用信号量(`asyncio.Semaphore`)控制最大并行连接数(默认10个)
- 异步执行所有连接任务,最大化利用资源
- 自动处理连接池,避免资源耗尽
2. **智能启动检测**:
- 检查systemd的`multi-user.target`状态确认系统完全启动
- 执行`uptime`命令获取系统运行时间
- 测量连接延迟作为性能指标
3. **健壮的重试机制**:
- 默认5次重试,每次间隔10秒
- 区分不同错误类型(连接超时、认证失败等)
- 提供详细的错误信息帮助诊断问题
4. **自动化配置生成**:
- 根据命名模式自动生成虚拟机配置(引用[2])
- 支持自定义主机名前缀和IP地址范围
- 生成符合个人审美的命名方案(如ubuntu-101)
## 高级功能扩展
### 1. SSH隧道与端口转发(引用[1][3])
```python
async def create_ssh_tunnel(conn, local_port, remote_host, remote_port):
"""创建SSH隧道用于安全访问内部服务"""
try:
# 动态端口转发(引用[1])
# 或特定端口转发(引用[3])
tunnel = await conn.forward_local_port(
'localhost', local_port, remote_host, remote_port
)
print(f"隧道创建成功: localhost:{local_port} -> {remote_host}:{remote_port}")
return tunnel
except Exception as e:
print(f"隧道创建失败: {str(e)}")
return None
# 在连接成功后调用
# tunnel = await create_ssh_tunnel(conn, 3306, "db.internal", 3306)
```
### 2. 集成Xshell自动化(引用[4])
```python
def generate_xshell_sessions(vm_configs):
"""生成Xshell会话配置文件"""
config = f"""[Xshell Sessions]
[Session1]
Host={vm_configs[0]['host']}
Port=22
Username={vm_configs[0]['username']}
Password={vm_configs[0]['password']}
Protocol=SSH
"""
# 保存为.xsh文件
with open("vm_sessions.xsh", "w") as f:
f.write(config)
print("Xshell会话配置文件已生成")
```
### 3. 启动过程监控
```python
async def monitor_boot_process(conn):
"""监控系统启动日志"""
try:
async with conn.create_process("journalctl -b -f") as process:
async for line in process.stdout:
if "Startup finished" in line:
print("系统启动完成:", line.strip())
break
elif "failed" in line.lower() or "error" in line.lower():
print("启动错误:", line.strip())
except Exception as e:
print(f"启动监控失败: {str(e)}")
```
## 执行流程说明
1. **配置生成**:使用`generate_vm_config`自动创建虚拟机配置
2. **并行检查**:通过`parallel_vm_boot_check`同时连接多台虚拟机
3. **状态验证**:每台虚拟机执行:
- SSH连接测试
- 系统服务状态检查(`multi-user.target`)
- 运行时间获取(`uptime`)
- 连接延迟测量
4. **结果报告**:生成表格化报告并保存到文件
5. **隧道创建**(可选):为需要访问内部服务的虚拟机建立SSH隧道
## 典型输出示例
```
虚拟机启动状态检查结果:
================================================================================
主机名/IP | 状态 | 详细信息
--------------------------------------------------------------------------------
ubuntu-101 (192.168.1.101) | ✓ 成功 | 启动成功 | 延迟: 0.45s | 运行时间: up 5 minutes
ubuntu-102 (192.168.1.102) | ✗ 失败 | 连接失败: 超过最大重试次数(5)
ubuntu-103 (192.168.1.103) | ✓ 成功 | 启动成功 | 延迟: 0.52s | 运行时间: up 2 hours
ubuntu-104 (192.168.1.104) | ✗ 失败 | 认证失败: 用户名或密码错误
ubuntu-105 (192.168.1.105) | ✓ 成功 | 启动成功 | 延迟: 0.38s | 运行时间: up 10 minutes
================================================================================
检查完成 | 总耗时: 12.75秒
成功: 3/5 | 失败: 2/5
结果已保存到 vm_boot_report.txt
```
此解决方案提供了完整的虚拟机启动状态检查流程,结合了引用中的隧道技术[^1]、自动化配置[^2]和高效运维理念[^4],特别适合大规模虚拟机环境的管理。