aiida-ssh2win 完全指南及实战案例
一、aiida-ssh2win 是什么
aiida-ssh2win是 AiiDA 生态中的一个传输插件(transport plugin),用于让运行在 Linux 或 macOS 上的 AiiDA 主节点,通过 SSH 连接到Windows 主机,并在 Windows 上执行命令、上传下载文件。它解决的核心问题是:AiiDA 原生的 SSH 传输层默认假设远端是类 Unix 系统,而 Windows 的默认 Shell(cmd.exe / PowerShell)在路径分隔符、命令语法、退出码、文件权限等方面与 Unix 差异很大,直接复用会导致大量兼容性问题。
简单来说,它把「AiiDA 的远程计算资源」这一抽象,扩展到了 Windows 机器上,使你可以把 Windows 上的计算软件(如 VASP、Quantum ESPRESSO 的 Windows 编译版、Materials Studio、自研的 .exe 程序)纳入 AiiDA 的工作流管理。
二、核心功能概览
- SSH 连接管理:基于 Paramiko 建立到 Windows 主机的 SSH 会话,支持密码与密钥认证。
- 命令执行:在 Windows 上通过 cmd.exe 或 PowerShell 执行命令,并正确解析退出码。
- 文件传输:基于 SFTP 实现上传、下载、目录创建、删除、重命名等操作。
- 路径适配:自动处理 Windows 的盘符路径(如
C:\Users\aiida\work)与 POSIX 路径之间的转换。 - 与 AiiDA Computer 集成:注册为 AiiDA 的 transport 类型,可直接在
verdi computer setup中选用。 - 作业调度对接:配合
direct调度器(Windows 上通常没有 Slurm/PBS),实现「提交即执行」。
三、安装与环境准备
3.1 前置条件
| 项目 | 要求 |
|---|---|
| AiiDA 版本 | aiida-core >= 2.0 |
| Python | 3.8 及以上 |
| Windows 端 | Windows 10/11 或 Windows Server,已启用 OpenSSH Server |
| 网络 | AiiDA 主节点可 SSH 访问 Windows 主机的 22 端口 |
3.2 在 Windows 上启用 OpenSSH Server
以管理员身份打开 PowerShell,执行:
Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0 Start-Service sshd Set-Service -Name sshd -StartupType 'Automatic' New-NetFirewallRule -Name sshd -DisplayName 'OpenSSH Server' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22随后确认默认 Shell。若希望使用 PowerShell 而非 cmd.exe,可执行:
New-ItemProperty -Path "HKLM:\SOFTWARE\OpenSSH" -Name DefaultShell ` -Value "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -PropertyType String -Force3.3 安装插件
pip install aiida-ssh2win verdi plugin list aiida.transports若输出中出现ssh2win,说明插件已注册成功。
3.4 注册 AiiDA Computer
verdi computer setup \ --label win-node-01 \ --hostname 192.168.1.50 \ --transport ssh2win \ --scheduler direct \ --work-dir 'C:\aiida\work' \ --mpirun-command 'mpiexec -n {tot_num_mpiprocs}' \ --shebang ' ' verdi computer configure ssh2win win-node-01配置过程中会提示输入用户名、端口、密钥路径、连接超时等参数。完成后用verdi computer test win-node-01验证连通性。
四、语法与参数详解
4.1 传输层配置参数
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
hostname | str | — | Windows 主机地址或域名 |
port | int | 22 | SSH 端口 |
username | str | — | Windows 登录用户名 |
password | str | None | 密码认证时使用,建议改用密钥 |
key_filename | str | None | 私钥文件路径 |
key_policy | str | AutoAddPolicy | 主机密钥策略,生产环境建议 RejectPolicy |
timeout | int | 60 | 连接与命令执行超时(秒) |
allow_agent | bool | True | 是否使用 SSH Agent |
look_for_keys | bool | True | 是否自动搜索 ~/.ssh 下的密钥 |
shell | str | cmd | 远端 Shell 类型,可选cmd或powershell |
use_login_shell | bool | False | 是否以登录 Shell 方式执行 |
path_separator | str | \ | 远端路径分隔符 |
4.2 常用方法
from aiida.transports.plugins.ssh2win import Ssh2WinTransport with Ssh2WinTransport(hostname='192.168.1.50', username='aiida', key_filename='/home/aiida/.ssh/id_rsa') as transport: # 执行命令 retcode, stdout, stderr = transport.exec_command_wait('dir C:\\aiida\\work') # 文件操作 transport.putfile('/local/input.in', 'C:\\aiida\\work\\input.in') transport.getfile('C:\\aiida\\work\\output.out', '/local/output.out') # 目录操作 transport.mkdir('C:\\aiida\\work\\calc_001') transport.chdir('C:\\aiida\\work\\calc_001') print(transport.getcwd()) # 判断与列举 print(transport.isdir('C:\\aiida\\work')) print(transport.listdir('C:\\aiida\\work'))4.3 关键方法说明
| 方法 | 返回值 | 说明 |
|---|---|---|
exec_command_wait(cmd) | (retcode, stdout, stderr) | 同步执行命令并等待结束 |
putfile(src, dst) | None | 上传单个文件 |
puttree(src, dst) | None | 递归上传目录 |
getfile(src, dst) | None | 下载单个文件 |
gettree(src, dst) | None | 递归下载目录 |
mkdir(path) | None | 创建目录,已存在时抛异常 |
rmtree(path) | None | 递归删除目录 |
copyfile(src, dst) | None | 远端内部复制文件 |
rename(src, dst) | None | 重命名或移动 |
path_exists(path) | bool | 判断路径是否存在 |
isdir / isfile | bool | 类型判断 |
get_attribute(path) | dict | 获取大小、修改时间等属性 |
五、16 个实际应用案例
案例 1:验证 Windows 主机连通性
from aiida.transports.plugins.ssh2win import Ssh2WinTransport with Ssh2WinTransport(hostname='192.168.1.50', username='aiida', key_filename='/home/aiida/.ssh/id_rsa') as t: code, out, err = t.exec_command_wait('hostname') print(code, out.strip())用于排查网络、认证与 Shell 是否正常,是最基础的健康检查。
案例 2:在 Windows 上创建计算工作目录
with Ssh2WinTransport(hostname='192.168.1.50', username='aiida') as t: workdir = r'C:\aiida\work\calc_20240101' if not t.path_exists(workdir): t.mkdir(workdir) t.chdir(workdir) print(t.getcwd())案例 3:上传单个输入文件
with Ssh2WinTransport(hostname='192.168.1.50', username='aiida') as t: t.putfile('/home/aiida/inputs/INCAR', r'C:\aiida\work\calc_001\INCAR')案例 4:递归上传整个输入目录
with Ssh2WinTransport(hostname='192.168.1.50', username='aiida') as t: t.puttree('/home/aiida/inputs/vasp_case', r'C:\aiida\work\vasp_case')适合包含多个输入文件(INCAR、POSCAR、POTCAR、KPOINTS)的场景。
案例 5:下载计算结果文件
with Ssh2WinTransport(hostname='192.168.1.50', username='aiida') as t: t.getfile(r'C:\aiida\work\calc_001\OUTCAR', '/home/aiida/results/OUTCAR')案例 6:递归下载整个输出目录
with Ssh2WinTransport(hostname='192.168.1.50', username='aiida') as t: t.gettree(r'C:\aiida\work\calc_001', '/home/aiida/results/calc_001')案例 7:执行 Windows 原生可执行程序
with Ssh2WinTransport(hostname='192.168.1.50', username='aiida') as t: t.chdir(r'C:\aiida\work\calc_001') code, out, err = t.exec_command_wait(r'C:\software\vasp.exe') print('retcode =', code) print(out[:500])案例 8:使用 PowerShell 执行脚本
with Ssh2WinTransport(hostname='192.168.1.50', username='aiida', shell='powershell') as t: code, out, err = t.exec_command_wait( 'Get-ChildItem C:\\aiida\\work | Measure-Object | Select-Object -ExpandProperty Count') print('文件数量:', out.strip())案例 9:通过 mpiexec 并行运行
with Ssh2WinTransport(hostname='192.168.1.50', username='aiida') as t: t.chdir(r'C:\aiida\work\calc_001') code, out, err = t.exec_command_wait( r'mpiexec -n 8 C:\software\vasp_std.exe') print(code)案例 10:检查远端文件是否存在并读取大小
with Ssh2WinTransport(hostname='192.168.1.50', username='aiida') as t: path = r'C:\aiida\work\calc_001\OUTCAR' if t.path_exists(path): attr = t.get_attribute(path) print('大小:', attr.get('st_size'), '字节')案例 11:列出目录内容并筛选
with Ssh2WinTransport(hostname='192.168.1.50', username='aiida') as t: files = t.listdir(r'C:\aiida\work\calc_001') outcars = [f for f in files if f.lower().endswith('.outcar')] print(outcars)案例 12:远端复制与重命名结果文件
with Ssh2WinTransport(hostname='192.168.1.50', username='aiida') as t: src = r'C:\aiida\work\calc_001\OUTCAR' bak = r'C:\aiida\work\calc_001\OUTCAR.bak' t.copyfile(src, bak) t.rename(bak, r'C:\aiida\work\calc_001\OUTCAR_20240101.bak')案例 13:清理临时目录
with Ssh2WinTransport(hostname='192.168.1.50', username='aiida') as t: tmp = r'C:\aiida\work\tmp' if t.path_exists(tmp): t.rmtree(tmp) t.mkdir(tmp)案例 14:在 AiiDA CalcJob 中使用
from aiida.engine import CalcJob from aiida.common.datastructures import CalcInfo, CodeInfo class WindowsVaspJob(CalcJob): @classmethod def define(cls, spec): super().define(spec) spec.input('parameters', valid_type=orm.Dict) spec.input('structure', valid_type=orm.StructureData) spec.output('output_parameters', valid_type=orm.Dict) def prepare_for_submission(self, folder): folder.write_text_file('INCAR', self.inputs.parameters.get_dict()['incar']) calcinfo = CalcInfo() calcinfo.codes_info = [CodeInfo()] calcinfo.codes_info[0].cmdline = [r'C:\software\vasp_std.exe'] calcinfo.retrieve_list = ['OUTCAR', 'OSZICAR'] return calcinfo配合direct调度器与ssh2win传输层,即可把 Windows 上的 VASP 纳入 AiiDA 工作流。
案例 15:批量提交多个计算任务
from aiida.engine import submit from aiida.orm import load_code, Dict code = load_code('vasp-win@win-node-01') for i, params in enumerate(param_list): inputs = {'code': code, 'parameters': Dict(dict=params)} node = submit(WindowsVaspJob, **inputs) print(f'任务 {i} 已提交,PK = {node.pk}')案例 16:异常重试与日志记录
import logging, time from aiida.transports.plugins.ssh2win import Ssh2WinTransport logger = logging.getLogger(__name__) def run_with_retry(cmd, retries=3, delay=5): for attempt in range(1, retries + 1): try: with Ssh2WinTransport(hostname='192.168.1.50', username='aiida', timeout=120) as t: code, out, err = t.exec_command_wait(cmd) if code == 0: return out logger.warning('第 %d 次执行失败,retcode=%d, stderr=%s', attempt, code, err) except Exception as exc: logger.error('第 %d 次连接异常: %s', attempt, exc) time.sleep(delay) raise RuntimeError(f'命令执行失败: {cmd}')六、常见错误与排查
| 错误现象 | 可能原因 | 解决方案 |
|---|---|---|
Connection refused | Windows 未启动 sshd 或防火墙拦截 | 启动 sshd 服务并放行 22 端口 |
Authentication failed | 用户名格式错误或密钥未授权 | Windows 用户名区分大小写;将公钥写入C:\Users\<user>\.ssh\authorized_keys |
FileNotFoundError: C:\... | 路径分隔符被转义错误 | 使用原始字符串r'C:\path'或双反斜杠 |
| 命令返回码恒为 0 | cmd.exe 不传递退出码 | 改用 PowerShell 或在命令后追加&& exit /b %ERRORLEVEL% |
| 中文路径乱码 | 编码不一致 | 统一使用 UTF-8,或在 PowerShell 中设置[Console]::OutputEncoding |
Permission denied写入失败 | Windows 目录 ACL 限制 | 为 aiida 用户授予工作目录的写权限 |
| 上传大文件超时 | 默认 timeout 过短 | 调大timeout参数至 300 以上 |
Host key verification failed | 主机密钥未记录 | 首次连接时确认指纹,或配置known_hosts |
七、使用注意事项
- 优先使用密钥认证:密码认证在自动化场景中不安全,且部分 Windows 配置下不稳定。
- 路径统一用原始字符串:Python 中
\n、\t会被转义,务必使用r'C:\path'。 - 明确 Shell 类型:cmd 与 PowerShell 的命令语法差异大,配置时显式指定
shell参数。 - 退出码不可靠时显式判断:建议在命令末尾追加退出码输出,或解析 stdout 中的关键字。
- 避免在 Windows 上依赖 Unix 权限模型:AiiDA 的
chmod相关操作在 Windows 上可能无效,需在插件层做兼容处理。 - 调度器选择 direct:Windows 上通常没有 Slurm/PBS,使用
direct调度器最稳妥。 - 注意并发连接数:Windows OpenSSH 对并发会话有限制,批量提交时建议控制并发。
- 日志与审计:开启 AiiDA 的 transport 日志,便于排查远端执行问题。
- 版本兼容性:升级 aiida-core 后需确认插件版本匹配,避免 API 变更导致失败。
八、总结
aiida-ssh2win填补了 AiiDA 在 Windows 计算资源管理上的空白。通过它,你可以把 Windows 上的商业软件、自研程序与 AiiDA 的工作流、 provenance 追踪能力结合起来。掌握其配置参数、路径处理规则与退出码语义,是稳定运行的关键。建议先在测试机上跑通本文的 16 个案例,再逐步迁移到生产环境。
《AI提示工程必知必会》为读者提供了丰富的AI提示工程知识与实战技能,主要包括各类提示词的应用,如问答式、指令式、状态类、建议式、安全类和感谢类提示词,以及如何通过实战演练掌握提示词的使用技巧;使用提示词进行文本摘要、改写重述、语法纠错、机器翻译等语言处理任务,以及在数据挖掘、程序开发等领域的应用;AI在绘画创作上的应用,百度文心一言和阿里通义大模型这两大智能平台的特性与功能,以及市场调研中提示词的实战应用。通过阅读《AI提示工程必知必会》,读者可掌握如何有效利用AI提示工程提升工作效率,创新工作流程,并在职场中脱颖而出。