hpc-task 0.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hpc_task-0.0.1/LICENSE +21 -0
- hpc_task-0.0.1/PKG-INFO +40 -0
- hpc_task-0.0.1/README.md +16 -0
- hpc_task-0.0.1/hpc_task/__init__.py +0 -0
- hpc_task-0.0.1/hpc_task/hpc.py +147 -0
- hpc_task-0.0.1/hpc_task.egg-info/PKG-INFO +40 -0
- hpc_task-0.0.1/hpc_task.egg-info/SOURCES.txt +11 -0
- hpc_task-0.0.1/hpc_task.egg-info/dependency_links.txt +1 -0
- hpc_task-0.0.1/hpc_task.egg-info/requires.txt +1 -0
- hpc_task-0.0.1/hpc_task.egg-info/top_level.txt +1 -0
- hpc_task-0.0.1/setup.cfg +4 -0
- hpc_task-0.0.1/setup.py +26 -0
- hpc_task-0.0.1/tests/test_task.py +94 -0
hpc_task-0.0.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 pj.ren
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
hpc_task-0.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hpc_task
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: HPC Task python library.
|
|
5
|
+
Home-page: https://gitee.com/pjren/hpc_task
|
|
6
|
+
Author: Renpj
|
|
7
|
+
Author-email: 0403114076@163.com
|
|
8
|
+
License: MIT
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Requires-Dist: paramiko
|
|
14
|
+
Dynamic: author
|
|
15
|
+
Dynamic: author-email
|
|
16
|
+
Dynamic: classifier
|
|
17
|
+
Dynamic: description
|
|
18
|
+
Dynamic: description-content-type
|
|
19
|
+
Dynamic: home-page
|
|
20
|
+
Dynamic: license
|
|
21
|
+
Dynamic: license-file
|
|
22
|
+
Dynamic: requires-dist
|
|
23
|
+
Dynamic: summary
|
|
24
|
+
|
|
25
|
+
# HPC Task
|
|
26
|
+
|
|
27
|
+
Python package for easy HPC task management based on paramiko.
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
pip install -U hpc_task
|
|
32
|
+
|
|
33
|
+
**Requirements**
|
|
34
|
+
* paramiko
|
|
35
|
+
|
|
36
|
+
## Usage
|
|
37
|
+
|
|
38
|
+
See tests
|
|
39
|
+
|
|
40
|
+
## TODO
|
hpc_task-0.0.1/README.md
ADDED
|
File without changes
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
import paramiko
|
|
4
|
+
from paramiko import SSHClient, SSHException
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class HPCTask:
|
|
8
|
+
def __init__(self, workdir='.'):
|
|
9
|
+
self._ssh_jump = None
|
|
10
|
+
self.jobid = None
|
|
11
|
+
self.ssh_client = None
|
|
12
|
+
self.workdir = workdir
|
|
13
|
+
|
|
14
|
+
def connect(self, target_host, jump_host=None):
|
|
15
|
+
"""
|
|
16
|
+
开启队列
|
|
17
|
+
:return: jobid
|
|
18
|
+
"""
|
|
19
|
+
# 建立 ssh 连接
|
|
20
|
+
# 跳板机连接信息
|
|
21
|
+
if self.ssh_client is None:
|
|
22
|
+
try:
|
|
23
|
+
# 使用隧道连接到目标服务器
|
|
24
|
+
target_client = SSHClient()
|
|
25
|
+
target_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
|
26
|
+
# 首先连接到跳板机
|
|
27
|
+
if jump_host is not None:
|
|
28
|
+
# 创建SSH客户端
|
|
29
|
+
jump_client = SSHClient()
|
|
30
|
+
jump_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
|
31
|
+
jump_client.connect(**jump_host)
|
|
32
|
+
# 在跳板机上创建到目标服务器的隧道
|
|
33
|
+
transport = jump_client.get_transport()
|
|
34
|
+
dest_addr = (target_host['hostname'], target_host['port'])
|
|
35
|
+
local_addr = ('127.0.0.1', 0) # 本地任意端口
|
|
36
|
+
channel = transport.open_channel('direct-tcpip', dest_addr, local_addr)
|
|
37
|
+
target_client.connect('127.0.0.1',
|
|
38
|
+
port=channel.getpeername()[1],
|
|
39
|
+
username=target_host['username'],
|
|
40
|
+
password=target_host['password'],
|
|
41
|
+
sock=channel)
|
|
42
|
+
self._ssh_jump = jump_client
|
|
43
|
+
else:
|
|
44
|
+
target_client.connect(**target_host)
|
|
45
|
+
|
|
46
|
+
self.ssh_client = target_client
|
|
47
|
+
print("Connect to SSH success.")
|
|
48
|
+
except SSHException as e:
|
|
49
|
+
raise e
|
|
50
|
+
|
|
51
|
+
def prerun(self):
|
|
52
|
+
# 提交任务,占据节点
|
|
53
|
+
# TODO: 命令改成配置,适配常见 hpc
|
|
54
|
+
|
|
55
|
+
if self.ssh_client is None:
|
|
56
|
+
raise RuntimeError('ssh client is not connected')
|
|
57
|
+
commands = [f'mkdir -p {self.workdir}',
|
|
58
|
+
f'cd {self.workdir}',
|
|
59
|
+
f'cp ~/bin/hpc_job.chess hpc_job.chess',
|
|
60
|
+
f'bsub < hpc_job.chess']
|
|
61
|
+
stdin, stdout, stderr = self.ssh_client.exec_command(';'.join(commands))
|
|
62
|
+
jobid = stdout.read().decode().strip() # "Job <688518> is submitted to queue <proj>."
|
|
63
|
+
jobid = jobid.split()[1].lstrip('<').rstrip('>')
|
|
64
|
+
self.jobid = jobid
|
|
65
|
+
return stdin, stdout, stderr
|
|
66
|
+
|
|
67
|
+
def postrun(self):
|
|
68
|
+
"""
|
|
69
|
+
说明:关闭任务节点占用
|
|
70
|
+
bkill JOBID是从任务头部开始杀, KILL 信号会传递到子进程
|
|
71
|
+
pkill gosh-remote 是直接杀
|
|
72
|
+
二者可能相同, 也可能不相同. 取决于 bsub 时如何定义的.
|
|
73
|
+
通常 bsub 是用一个 script 调 gosh-remote, 这时二者就不同了.
|
|
74
|
+
那个主调script 可能会做信号处理, 会顺着 gosh-remote的调用进程下去, 逐一 KILL.
|
|
75
|
+
gosh-remote我不记得是否有信号处理的逻辑, 得做实验确认一下.
|
|
76
|
+
|
|
77
|
+
:return:
|
|
78
|
+
|
|
79
|
+
TODO: 命令改成配置,适配常见 hpc
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
if self.ssh_client is None:
|
|
83
|
+
raise RuntimeError('ssh client is not connected')
|
|
84
|
+
commands = ['sleep 1', 'pkill gosh-remote', f'bkill {self.jobid}']
|
|
85
|
+
stdin, stdout, stderr = self.ssh_client.exec_command(';'.join(commands))
|
|
86
|
+
return stdin, stdout, stderr
|
|
87
|
+
|
|
88
|
+
@property
|
|
89
|
+
def status(self):
|
|
90
|
+
"""
|
|
91
|
+
查询作业状态:排队,运行,结束
|
|
92
|
+
# TODO: 统一不同的排队系统状态码
|
|
93
|
+
:return:
|
|
94
|
+
"""
|
|
95
|
+
if self.ssh_client is None:
|
|
96
|
+
raise RuntimeError('ssh client is not connected')
|
|
97
|
+
commands = [f'bjobs -noheader {self.jobid}',]
|
|
98
|
+
stdin, stdout, stderr = self.ssh_client.exec_command(';'.join(commands))
|
|
99
|
+
stat = stdout.read().decode().strip().split() # 688559 renpeng RUN proj Khpcserver0 72*Knode44 scheduler Sep 8 10:55
|
|
100
|
+
if len(stat) > 5:
|
|
101
|
+
return stat[2]
|
|
102
|
+
else:
|
|
103
|
+
return "UNKNOWN"
|
|
104
|
+
|
|
105
|
+
def submit(self):
|
|
106
|
+
"""
|
|
107
|
+
|
|
108
|
+
:return: None
|
|
109
|
+
"""
|
|
110
|
+
return None
|
|
111
|
+
|
|
112
|
+
def upload(self):
|
|
113
|
+
"""
|
|
114
|
+
TODO: 使用 rsync
|
|
115
|
+
:return: file sync status
|
|
116
|
+
"""
|
|
117
|
+
if self.ssh_client is None:
|
|
118
|
+
raise RuntimeError('ssh client is not connected')
|
|
119
|
+
|
|
120
|
+
# 创建服务器目录,如果不存在
|
|
121
|
+
self.ssh_client.exec_command(f'if [ ! -d "{self.workdir}" ]; then mkdir -p {self.workdir};fi')
|
|
122
|
+
sftp_client = self.ssh_client.open_sftp()
|
|
123
|
+
filelist = os.listdir(self.workdir) # TODO: 递归所有文件夹
|
|
124
|
+
for filename in filelist:
|
|
125
|
+
sftp_client.put(os.path.join(self.workdir,filename), os.path.join(self.workdir,filename), confirm=False)
|
|
126
|
+
sftp_client.close()
|
|
127
|
+
return None
|
|
128
|
+
|
|
129
|
+
def download(self):
|
|
130
|
+
if self.ssh_client is None:
|
|
131
|
+
raise RuntimeError('ssh client is not connected')
|
|
132
|
+
|
|
133
|
+
sftp_client = self.ssh_client.open_sftp()
|
|
134
|
+
for filename in sftp_client.listdir(self.workdir): # TODO: 递归所有文件夹
|
|
135
|
+
sftp_client.get(os.path.join(self.workdir,filename), os.path.join(self.workdir,filename))
|
|
136
|
+
sftp_client.close()
|
|
137
|
+
return None
|
|
138
|
+
|
|
139
|
+
def close(self):
|
|
140
|
+
"""
|
|
141
|
+
关闭队列
|
|
142
|
+
"""
|
|
143
|
+
if self.ssh_client is not None:
|
|
144
|
+
self.ssh_client.close()
|
|
145
|
+
if self._ssh_jump is not None:
|
|
146
|
+
self._ssh_jump.close()
|
|
147
|
+
return None
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hpc_task
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: HPC Task python library.
|
|
5
|
+
Home-page: https://gitee.com/pjren/hpc_task
|
|
6
|
+
Author: Renpj
|
|
7
|
+
Author-email: 0403114076@163.com
|
|
8
|
+
License: MIT
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Requires-Dist: paramiko
|
|
14
|
+
Dynamic: author
|
|
15
|
+
Dynamic: author-email
|
|
16
|
+
Dynamic: classifier
|
|
17
|
+
Dynamic: description
|
|
18
|
+
Dynamic: description-content-type
|
|
19
|
+
Dynamic: home-page
|
|
20
|
+
Dynamic: license
|
|
21
|
+
Dynamic: license-file
|
|
22
|
+
Dynamic: requires-dist
|
|
23
|
+
Dynamic: summary
|
|
24
|
+
|
|
25
|
+
# HPC Task
|
|
26
|
+
|
|
27
|
+
Python package for easy HPC task management based on paramiko.
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
pip install -U hpc_task
|
|
32
|
+
|
|
33
|
+
**Requirements**
|
|
34
|
+
* paramiko
|
|
35
|
+
|
|
36
|
+
## Usage
|
|
37
|
+
|
|
38
|
+
See tests
|
|
39
|
+
|
|
40
|
+
## TODO
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
setup.py
|
|
4
|
+
hpc_task/__init__.py
|
|
5
|
+
hpc_task/hpc.py
|
|
6
|
+
hpc_task.egg-info/PKG-INFO
|
|
7
|
+
hpc_task.egg-info/SOURCES.txt
|
|
8
|
+
hpc_task.egg-info/dependency_links.txt
|
|
9
|
+
hpc_task.egg-info/requires.txt
|
|
10
|
+
hpc_task.egg-info/top_level.txt
|
|
11
|
+
tests/test_task.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
paramiko
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
hpc_task
|
hpc_task-0.0.1/setup.cfg
ADDED
hpc_task-0.0.1/setup.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from setuptools import setup
|
|
2
|
+
|
|
3
|
+
with open("README.md", "r", encoding='utf-8') as f:
|
|
4
|
+
long_description = f.read()
|
|
5
|
+
|
|
6
|
+
install_requires = [
|
|
7
|
+
'paramiko',
|
|
8
|
+
]
|
|
9
|
+
|
|
10
|
+
setup(
|
|
11
|
+
name='hpc_task',
|
|
12
|
+
version='0.0.1',
|
|
13
|
+
packages=['hpc_task'],
|
|
14
|
+
url='https://gitee.com/pjren/hpc_task',
|
|
15
|
+
license='MIT',
|
|
16
|
+
author='Renpj',
|
|
17
|
+
author_email='0403114076@163.com',
|
|
18
|
+
description='HPC Task python library.',
|
|
19
|
+
long_description=long_description,
|
|
20
|
+
long_description_content_type="text/markdown",
|
|
21
|
+
install_requires=install_requires,
|
|
22
|
+
classifiers=[
|
|
23
|
+
"Programming Language :: Python :: 3",
|
|
24
|
+
"Operating System :: OS Independent",
|
|
25
|
+
],
|
|
26
|
+
)
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
import ase
|
|
5
|
+
from ase.calculators.vasp import Vasp
|
|
6
|
+
from hpc_task.hpc import HPCTask
|
|
7
|
+
|
|
8
|
+
jump_host = {
|
|
9
|
+
'hostname': os.getenv("JUMP_HOST_IP"),
|
|
10
|
+
'port': int(os.getenv("JUMP_HOST_PORT")),
|
|
11
|
+
'username': os.getenv("JUMP_HOST_USER"),
|
|
12
|
+
'password': os.getenv("JUMP_HOST_PASS"), # 建议使用密钥认证而非密码
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
target_host = {
|
|
16
|
+
'hostname': os.getenv("TARGET_HOST_IP"),
|
|
17
|
+
'port': int(os.getenv("TARGET_HOST_PORT")),
|
|
18
|
+
'username': os.getenv("TARGET_HOST_USER"),
|
|
19
|
+
'password': os.getenv("TARGET_HOST_PASS"), # 建议使用密钥认证而非密码
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
class TestHPCTask:
|
|
23
|
+
def setup_method(self):
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
def teardown_method(self):
|
|
27
|
+
pass
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def test_connect(self):
|
|
31
|
+
hpc = HPCTask()
|
|
32
|
+
hpc.connect(target_host, jump_host)
|
|
33
|
+
stdin, stdout, stderr = hpc.ssh_client.exec_command('hostname')
|
|
34
|
+
print(stdout.read().decode().strip())
|
|
35
|
+
hpc.close()
|
|
36
|
+
|
|
37
|
+
def test_pre_post_run(self):
|
|
38
|
+
workdir = 'test_hpc_run'
|
|
39
|
+
hpc = HPCTask(workdir=workdir)
|
|
40
|
+
hpc.connect(target_host, jump_host)
|
|
41
|
+
stdin, stdout, stderr = hpc.prerun()
|
|
42
|
+
print(hpc.jobid)
|
|
43
|
+
print(f"Job {hpc.jobid} status: {hpc.status}")
|
|
44
|
+
stdin, stdout, stderr = hpc.postrun()
|
|
45
|
+
print(stdout.read().decode().strip())
|
|
46
|
+
|
|
47
|
+
def test_upload(self):
|
|
48
|
+
workdir = 'test_hpc_run'
|
|
49
|
+
hpc = HPCTask(workdir=workdir)
|
|
50
|
+
hpc.connect(target_host, jump_host)
|
|
51
|
+
# 在本地创建 workdir
|
|
52
|
+
if not os.path.exists(workdir):
|
|
53
|
+
os.makedirs(workdir)
|
|
54
|
+
# 新建一个空文件
|
|
55
|
+
with open(os.path.join(workdir, 'test.xyz'), 'w') as f:
|
|
56
|
+
f.write('test')
|
|
57
|
+
hpc.upload()
|
|
58
|
+
|
|
59
|
+
def test_download(self):
|
|
60
|
+
workdir = 'test_hpc_run'
|
|
61
|
+
hpc = HPCTask(workdir=workdir)
|
|
62
|
+
hpc.connect(target_host, jump_host)
|
|
63
|
+
if not os.path.exists(workdir):
|
|
64
|
+
os.makedirs(workdir)
|
|
65
|
+
hpc.download()
|
|
66
|
+
|
|
67
|
+
def test_vasp_calc(self):
|
|
68
|
+
atoms = ase.Atoms('N2', positions=[(0.,0.,0.),(1.4,0.,0.)],cell=[10,10,10], pbc=True)
|
|
69
|
+
workdir = 'test_vasp_calc'
|
|
70
|
+
curdir = os.path.abspath('.')
|
|
71
|
+
python_command = [ # TODO: 写成一个python 的 template
|
|
72
|
+
"from hpc_task.hpc import HPCTask",
|
|
73
|
+
"import os",
|
|
74
|
+
f"os.chdir('{curdir}')",
|
|
75
|
+
f"hpc = HPCTask(workdir='{workdir}')",
|
|
76
|
+
f"hpc.connect({target_host}, {jump_host})",
|
|
77
|
+
"hpc.upload()",
|
|
78
|
+
f"stdin,stdout,stderr = hpc.ssh_client.exec_command('cd {workdir};mpirun -np 2 vasp544_std')",
|
|
79
|
+
"print(stdout.read().decode().strip())",
|
|
80
|
+
"hpc.download()",
|
|
81
|
+
"hpc.close()",
|
|
82
|
+
]
|
|
83
|
+
calc_command = f'python -c "{';'.join(python_command)}"'
|
|
84
|
+
calc = Vasp(xc='pbe',
|
|
85
|
+
command=calc_command,
|
|
86
|
+
directory=workdir,
|
|
87
|
+
gamma=True,
|
|
88
|
+
encut=400,
|
|
89
|
+
lwave=False,
|
|
90
|
+
lcharg=False,
|
|
91
|
+
)
|
|
92
|
+
atoms.calc = calc
|
|
93
|
+
e = atoms.get_potential_energy()
|
|
94
|
+
print(f"Energy of N2 is {e} eV.")
|