dma-cli 0.1.0__tar.gz → 0.1.2__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.
- {dma_cli-0.1.0 → dma_cli-0.1.2}/PKG-INFO +28 -52
- {dma_cli-0.1.0 → dma_cli-0.1.2}/README.md +27 -51
- {dma_cli-0.1.0 → dma_cli-0.1.2}/config.example.yaml +1 -2
- {dma_cli-0.1.0 → dma_cli-0.1.2}/dma_cli/__init__.py +1 -1
- {dma_cli-0.1.0 → dma_cli-0.1.2}/dma_cli/client.py +11 -3
- {dma_cli-0.1.0 → dma_cli-0.1.2}/dma_cli/resources.py +6 -10
- {dma_cli-0.1.0 → dma_cli-0.1.2}/dma_cli/submit.py +11 -7
- {dma_cli-0.1.0 → dma_cli-0.1.2}/dma_cli.egg-info/PKG-INFO +28 -52
- {dma_cli-0.1.0 → dma_cli-0.1.2}/dma_cli.egg-info/SOURCES.txt +3 -1
- {dma_cli-0.1.0 → dma_cli-0.1.2}/job.example.json +1 -1
- {dma_cli-0.1.0 → dma_cli-0.1.2}/pyproject.toml +1 -1
- dma_cli-0.1.2/tests/test_biz_platform.py +27 -0
- dma_cli-0.1.2/tests/test_login.py +55 -0
- {dma_cli-0.1.0 → dma_cli-0.1.2}/batch.example.json +0 -0
- {dma_cli-0.1.0 → dma_cli-0.1.2}/dma_cli/__main__.py +0 -0
- {dma_cli-0.1.0 → dma_cli-0.1.2}/dma_cli/calc.py +0 -0
- {dma_cli-0.1.0 → dma_cli-0.1.2}/dma_cli/cli.py +0 -0
- {dma_cli-0.1.0 → dma_cli-0.1.2}/dma_cli/state.py +0 -0
- {dma_cli-0.1.0 → dma_cli-0.1.2}/dma_cli.egg-info/dependency_links.txt +0 -0
- {dma_cli-0.1.0 → dma_cli-0.1.2}/dma_cli.egg-info/entry_points.txt +0 -0
- {dma_cli-0.1.0 → dma_cli-0.1.2}/dma_cli.egg-info/requires.txt +0 -0
- {dma_cli-0.1.0 → dma_cli-0.1.2}/dma_cli.egg-info/top_level.txt +0 -0
- {dma_cli-0.1.0 → dma_cli-0.1.2}/input/calc.py +0 -0
- {dma_cli-0.1.0 → dma_cli-0.1.2}/input/case1/calc.py +0 -0
- {dma_cli-0.1.0 → dma_cli-0.1.2}/input/case2/calc.py +0 -0
- {dma_cli-0.1.0 → dma_cli-0.1.2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dma-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: DMA 任务提交命令行工具
|
|
5
5
|
Requires-Python: >=3.8
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -9,11 +9,7 @@ Requires-Dist: pyyaml
|
|
|
9
9
|
|
|
10
10
|
# DMA CLI —— 任务提交工具
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
与压测无关,只借用压测脚本里已验证的接口形态,不改动任何压测内容。
|
|
14
|
-
|
|
15
|
-
> **术语**:本工具的 `image`(镜像)即网页上的「软件目录」,底层接口为 `/softwares/deep-mining`,
|
|
16
|
-
> snapshot 用 `softwareId`/`softwareVersionId`。容器镜像接口(image/public|private)不参与 job 提交,但 `image list` 合并展示供参考。
|
|
12
|
+
深度挖掘算法(DMA)网页外的命令行提交工具,用于提交单个或批量任务。
|
|
17
13
|
|
|
18
14
|
## 安装
|
|
19
15
|
|
|
@@ -22,14 +18,6 @@ Requires-Dist: pyyaml
|
|
|
22
18
|
pip install dma-cli
|
|
23
19
|
dma --help
|
|
24
20
|
|
|
25
|
-
# 从源码安装
|
|
26
|
-
cd tools/dma-cli
|
|
27
|
-
pip install .
|
|
28
|
-
|
|
29
|
-
# 方式二:本地开发模式(可直接修改代码调试)
|
|
30
|
-
cd tools/dma-cli
|
|
31
|
-
pip install -e .
|
|
32
|
-
|
|
33
21
|
# 安装后 dma 命令即可全局使用
|
|
34
22
|
dma --help
|
|
35
23
|
```
|
|
@@ -64,12 +52,11 @@ cp config.example.yaml config.yaml
|
|
|
64
52
|
base_url: https://szc.test.dp.tech
|
|
65
53
|
auth:
|
|
66
54
|
email: "" # 验证码登录邮箱(留空则必须填 token)
|
|
67
|
-
captcha: "
|
|
55
|
+
captcha: "" # 留空,执行 dma login 时交互输入验证码
|
|
68
56
|
token: "" # 直接配 token(优先于 email 登录)
|
|
69
57
|
uexid: "" # 留空从 JWT 自动解析为 fe_<userId>
|
|
70
58
|
user_id: "" # 留空从 JWT 自动解析
|
|
71
59
|
defaults:
|
|
72
|
-
biz_platform: 2
|
|
73
60
|
name_prefix: "cli-job"
|
|
74
61
|
max_run_time: 15
|
|
75
62
|
max_run_time_unit: d
|
|
@@ -108,6 +95,11 @@ dma login
|
|
|
108
95
|
dma login --email you@example.com
|
|
109
96
|
```
|
|
110
97
|
|
|
98
|
+
`auth.captcha` 留空时,登录会提示 `请输入邮箱验证码:`,输入本次验证码后提交。
|
|
99
|
+
验证码不会保存到配置文件或登录缓存。若配置中已填写 `auth.captcha`,则使用该值;
|
|
100
|
+
已有配置中的旧验证码请清空,以启用交互输入。输入为空或取消输入时,不发送登录请求。
|
|
101
|
+
此命令只校验输入的验证码,不会自动发送验证码邮件。
|
|
102
|
+
|
|
111
103
|
解析 JWT 缓存 `token` / `user_id` / `uexid` 到 `~/.dma_cli/credentials.json`。也可跳过登录,直接在 `config.yaml` 填 `token`。
|
|
112
104
|
|
|
113
105
|
### whoami —— 查看当前状态
|
|
@@ -120,12 +112,19 @@ dma whoami
|
|
|
120
112
|
|
|
121
113
|
### project —— 项目管理
|
|
122
114
|
|
|
115
|
+
先查询当前账号可访问的项目,根据项目名称找到自己的 `projectId`,再设为当前项目:
|
|
116
|
+
|
|
123
117
|
```bash
|
|
124
|
-
dma project list
|
|
125
|
-
dma project use
|
|
126
|
-
dma project current
|
|
118
|
+
dma project list # 列出 projectId | name
|
|
119
|
+
dma project use <你的项目ID> # 替换为上一步查到的 projectId
|
|
120
|
+
dma project current # 确认当前项目 ID 和名称
|
|
127
121
|
```
|
|
128
122
|
|
|
123
|
+
`<你的项目ID>` 是占位符,执行前请替换为列表中你要使用的数字 ID,不要输入尖括号。
|
|
124
|
+
当前项目会保存在本机,后续提交任务时省略 `--project_id` 或设置为 `0` 即使用该项目;
|
|
125
|
+
JSON 配置中的 `"project_id": 0` 含义相同。`0` 表示使用当前项目,不是真实项目 ID,首次提交前需先完成项目选择。
|
|
126
|
+
如需单次提交到其他项目,可传入 `--project_id <你的项目ID>`,不会改变已保存的当前项目。
|
|
127
|
+
|
|
129
128
|
### sku —— 机型查询
|
|
130
129
|
|
|
131
130
|
```bash
|
|
@@ -158,7 +157,7 @@ dma job submit -i job.example.json
|
|
|
158
157
|
dma job submit \
|
|
159
158
|
--machine_type c2_m2_cpu \
|
|
160
159
|
--image_address ubuntu:22.04-py3.10-terminal-20260916 \
|
|
161
|
-
--project_id
|
|
160
|
+
--project_id 0 \
|
|
162
161
|
--command "python calc.py" \
|
|
163
162
|
--job_name my-job
|
|
164
163
|
```
|
|
@@ -175,7 +174,6 @@ dma job submit \
|
|
|
175
174
|
| `--machine_type` | `-t` | 机器类型名(自动匹配) |
|
|
176
175
|
| `--command` | `-c` | 执行命令 |
|
|
177
176
|
| `--max_run_time` | — | 最大运行时间(分钟) |
|
|
178
|
-
| `--nnode` | — | 节点数 |
|
|
179
177
|
| `--count` | — | 批量提交任务数 |
|
|
180
178
|
| `--concurrency` | — | 批量并发数 |
|
|
181
179
|
| `--batch_file` | — | 批量任务 JSON 数组文件 |
|
|
@@ -191,7 +189,8 @@ dma job submit \
|
|
|
191
189
|
dma job submit \
|
|
192
190
|
--machine_type c2_m2_cpu \
|
|
193
191
|
--image_address ubuntu:22.04-py3.10-terminal-20260916 \
|
|
194
|
-
--
|
|
192
|
+
--command "python calc.py" \
|
|
193
|
+
--count 5 \
|
|
195
194
|
--concurrency 5
|
|
196
195
|
```
|
|
197
196
|
|
|
@@ -204,7 +203,7 @@ dma job submit \
|
|
|
204
203
|
# {"job_name": "job-002", "machine_type": "c4_m8_cpu", ...}
|
|
205
204
|
# ]
|
|
206
205
|
|
|
207
|
-
dma job submit --batch_file batch.json --concurrency 3
|
|
206
|
+
dma job submit --batch_file batch.example.json --concurrency 3
|
|
208
207
|
```
|
|
209
208
|
|
|
210
209
|
### 方式三:--batch_input_dir(目录扫描)
|
|
@@ -212,9 +211,10 @@ dma job submit --batch_file batch.json --concurrency 3
|
|
|
212
211
|
```bash
|
|
213
212
|
# 每个子目录作为一个任务的输入
|
|
214
213
|
dma job submit \
|
|
215
|
-
--batch_input_dir ./
|
|
214
|
+
--batch_input_dir ./input \
|
|
216
215
|
--machine_type c2_m2_cpu \
|
|
217
216
|
--image_address ubuntu:22.04-py3.10-terminal-20260916 \
|
|
217
|
+
--command "python calc.py" \
|
|
218
218
|
--concurrency 5
|
|
219
219
|
```
|
|
220
220
|
|
|
@@ -228,9 +228,10 @@ cp config.example.yaml config.yaml
|
|
|
228
228
|
# 2. 查看状态
|
|
229
229
|
dma whoami
|
|
230
230
|
|
|
231
|
-
# 3.
|
|
231
|
+
# 3. 查询并选择自己的项目(将占位符替换为列表中的 projectId,不保留尖括号)
|
|
232
232
|
dma project list
|
|
233
|
-
dma project use
|
|
233
|
+
dma project use <你的项目ID>
|
|
234
|
+
dma project current
|
|
234
235
|
|
|
235
236
|
# 4. 查询机型和镜像
|
|
236
237
|
dma sku list --kind cpu
|
|
@@ -240,30 +241,5 @@ dma image list
|
|
|
240
241
|
dma job submit -i job.example.json
|
|
241
242
|
|
|
242
243
|
# 6. 批量提交
|
|
243
|
-
dma job submit --batch_file batch.json
|
|
244
|
-
```
|
|
245
|
-
|
|
246
|
-
## 文件结构
|
|
247
|
-
|
|
244
|
+
dma job submit --batch_file batch.example.json
|
|
248
245
|
```
|
|
249
|
-
tools/dma-cli/
|
|
250
|
-
├── README.md # 本文档
|
|
251
|
-
├── pyproject.toml # 打包配置
|
|
252
|
-
├── requirements.txt # requests, pyyaml
|
|
253
|
-
├── config.example.yaml # 配置模板
|
|
254
|
-
├── job.example.json # 任务配置示例
|
|
255
|
-
├── batch.example.json # 批量任务示例
|
|
256
|
-
└── dma_cli/ # Python 包目录
|
|
257
|
-
├── __init__.py
|
|
258
|
-
├── cli.py # 命令行入口
|
|
259
|
-
├── client.py # API 客户端
|
|
260
|
-
├── state.py # 状态管理
|
|
261
|
-
├── resources.py # 资源查询
|
|
262
|
-
├── submit.py # 任务提交
|
|
263
|
-
└── calc.py # 默认任务脚本
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
## 不在范围
|
|
267
|
-
|
|
268
|
-
- 不做多用户 / 不改压测脚本
|
|
269
|
-
- 不做分布式压测
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
# DMA CLI —— 任务提交工具
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
与压测无关,只借用压测脚本里已验证的接口形态,不改动任何压测内容。
|
|
5
|
-
|
|
6
|
-
> **术语**:本工具的 `image`(镜像)即网页上的「软件目录」,底层接口为 `/softwares/deep-mining`,
|
|
7
|
-
> snapshot 用 `softwareId`/`softwareVersionId`。容器镜像接口(image/public|private)不参与 job 提交,但 `image list` 合并展示供参考。
|
|
3
|
+
深度挖掘算法(DMA)网页外的命令行提交工具,用于提交单个或批量任务。
|
|
8
4
|
|
|
9
5
|
## 安装
|
|
10
6
|
|
|
@@ -13,14 +9,6 @@
|
|
|
13
9
|
pip install dma-cli
|
|
14
10
|
dma --help
|
|
15
11
|
|
|
16
|
-
# 从源码安装
|
|
17
|
-
cd tools/dma-cli
|
|
18
|
-
pip install .
|
|
19
|
-
|
|
20
|
-
# 方式二:本地开发模式(可直接修改代码调试)
|
|
21
|
-
cd tools/dma-cli
|
|
22
|
-
pip install -e .
|
|
23
|
-
|
|
24
12
|
# 安装后 dma 命令即可全局使用
|
|
25
13
|
dma --help
|
|
26
14
|
```
|
|
@@ -55,12 +43,11 @@ cp config.example.yaml config.yaml
|
|
|
55
43
|
base_url: https://szc.test.dp.tech
|
|
56
44
|
auth:
|
|
57
45
|
email: "" # 验证码登录邮箱(留空则必须填 token)
|
|
58
|
-
captcha: "
|
|
46
|
+
captcha: "" # 留空,执行 dma login 时交互输入验证码
|
|
59
47
|
token: "" # 直接配 token(优先于 email 登录)
|
|
60
48
|
uexid: "" # 留空从 JWT 自动解析为 fe_<userId>
|
|
61
49
|
user_id: "" # 留空从 JWT 自动解析
|
|
62
50
|
defaults:
|
|
63
|
-
biz_platform: 2
|
|
64
51
|
name_prefix: "cli-job"
|
|
65
52
|
max_run_time: 15
|
|
66
53
|
max_run_time_unit: d
|
|
@@ -99,6 +86,11 @@ dma login
|
|
|
99
86
|
dma login --email you@example.com
|
|
100
87
|
```
|
|
101
88
|
|
|
89
|
+
`auth.captcha` 留空时,登录会提示 `请输入邮箱验证码:`,输入本次验证码后提交。
|
|
90
|
+
验证码不会保存到配置文件或登录缓存。若配置中已填写 `auth.captcha`,则使用该值;
|
|
91
|
+
已有配置中的旧验证码请清空,以启用交互输入。输入为空或取消输入时,不发送登录请求。
|
|
92
|
+
此命令只校验输入的验证码,不会自动发送验证码邮件。
|
|
93
|
+
|
|
102
94
|
解析 JWT 缓存 `token` / `user_id` / `uexid` 到 `~/.dma_cli/credentials.json`。也可跳过登录,直接在 `config.yaml` 填 `token`。
|
|
103
95
|
|
|
104
96
|
### whoami —— 查看当前状态
|
|
@@ -111,12 +103,19 @@ dma whoami
|
|
|
111
103
|
|
|
112
104
|
### project —— 项目管理
|
|
113
105
|
|
|
106
|
+
先查询当前账号可访问的项目,根据项目名称找到自己的 `projectId`,再设为当前项目:
|
|
107
|
+
|
|
114
108
|
```bash
|
|
115
|
-
dma project list
|
|
116
|
-
dma project use
|
|
117
|
-
dma project current
|
|
109
|
+
dma project list # 列出 projectId | name
|
|
110
|
+
dma project use <你的项目ID> # 替换为上一步查到的 projectId
|
|
111
|
+
dma project current # 确认当前项目 ID 和名称
|
|
118
112
|
```
|
|
119
113
|
|
|
114
|
+
`<你的项目ID>` 是占位符,执行前请替换为列表中你要使用的数字 ID,不要输入尖括号。
|
|
115
|
+
当前项目会保存在本机,后续提交任务时省略 `--project_id` 或设置为 `0` 即使用该项目;
|
|
116
|
+
JSON 配置中的 `"project_id": 0` 含义相同。`0` 表示使用当前项目,不是真实项目 ID,首次提交前需先完成项目选择。
|
|
117
|
+
如需单次提交到其他项目,可传入 `--project_id <你的项目ID>`,不会改变已保存的当前项目。
|
|
118
|
+
|
|
120
119
|
### sku —— 机型查询
|
|
121
120
|
|
|
122
121
|
```bash
|
|
@@ -149,7 +148,7 @@ dma job submit -i job.example.json
|
|
|
149
148
|
dma job submit \
|
|
150
149
|
--machine_type c2_m2_cpu \
|
|
151
150
|
--image_address ubuntu:22.04-py3.10-terminal-20260916 \
|
|
152
|
-
--project_id
|
|
151
|
+
--project_id 0 \
|
|
153
152
|
--command "python calc.py" \
|
|
154
153
|
--job_name my-job
|
|
155
154
|
```
|
|
@@ -166,7 +165,6 @@ dma job submit \
|
|
|
166
165
|
| `--machine_type` | `-t` | 机器类型名(自动匹配) |
|
|
167
166
|
| `--command` | `-c` | 执行命令 |
|
|
168
167
|
| `--max_run_time` | — | 最大运行时间(分钟) |
|
|
169
|
-
| `--nnode` | — | 节点数 |
|
|
170
168
|
| `--count` | — | 批量提交任务数 |
|
|
171
169
|
| `--concurrency` | — | 批量并发数 |
|
|
172
170
|
| `--batch_file` | — | 批量任务 JSON 数组文件 |
|
|
@@ -182,7 +180,8 @@ dma job submit \
|
|
|
182
180
|
dma job submit \
|
|
183
181
|
--machine_type c2_m2_cpu \
|
|
184
182
|
--image_address ubuntu:22.04-py3.10-terminal-20260916 \
|
|
185
|
-
--
|
|
183
|
+
--command "python calc.py" \
|
|
184
|
+
--count 5 \
|
|
186
185
|
--concurrency 5
|
|
187
186
|
```
|
|
188
187
|
|
|
@@ -195,7 +194,7 @@ dma job submit \
|
|
|
195
194
|
# {"job_name": "job-002", "machine_type": "c4_m8_cpu", ...}
|
|
196
195
|
# ]
|
|
197
196
|
|
|
198
|
-
dma job submit --batch_file batch.json --concurrency 3
|
|
197
|
+
dma job submit --batch_file batch.example.json --concurrency 3
|
|
199
198
|
```
|
|
200
199
|
|
|
201
200
|
### 方式三:--batch_input_dir(目录扫描)
|
|
@@ -203,9 +202,10 @@ dma job submit --batch_file batch.json --concurrency 3
|
|
|
203
202
|
```bash
|
|
204
203
|
# 每个子目录作为一个任务的输入
|
|
205
204
|
dma job submit \
|
|
206
|
-
--batch_input_dir ./
|
|
205
|
+
--batch_input_dir ./input \
|
|
207
206
|
--machine_type c2_m2_cpu \
|
|
208
207
|
--image_address ubuntu:22.04-py3.10-terminal-20260916 \
|
|
208
|
+
--command "python calc.py" \
|
|
209
209
|
--concurrency 5
|
|
210
210
|
```
|
|
211
211
|
|
|
@@ -219,9 +219,10 @@ cp config.example.yaml config.yaml
|
|
|
219
219
|
# 2. 查看状态
|
|
220
220
|
dma whoami
|
|
221
221
|
|
|
222
|
-
# 3.
|
|
222
|
+
# 3. 查询并选择自己的项目(将占位符替换为列表中的 projectId,不保留尖括号)
|
|
223
223
|
dma project list
|
|
224
|
-
dma project use
|
|
224
|
+
dma project use <你的项目ID>
|
|
225
|
+
dma project current
|
|
225
226
|
|
|
226
227
|
# 4. 查询机型和镜像
|
|
227
228
|
dma sku list --kind cpu
|
|
@@ -231,30 +232,5 @@ dma image list
|
|
|
231
232
|
dma job submit -i job.example.json
|
|
232
233
|
|
|
233
234
|
# 6. 批量提交
|
|
234
|
-
dma job submit --batch_file batch.json
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
## 文件结构
|
|
238
|
-
|
|
235
|
+
dma job submit --batch_file batch.example.json
|
|
239
236
|
```
|
|
240
|
-
tools/dma-cli/
|
|
241
|
-
├── README.md # 本文档
|
|
242
|
-
├── pyproject.toml # 打包配置
|
|
243
|
-
├── requirements.txt # requests, pyyaml
|
|
244
|
-
├── config.example.yaml # 配置模板
|
|
245
|
-
├── job.example.json # 任务配置示例
|
|
246
|
-
├── batch.example.json # 批量任务示例
|
|
247
|
-
└── dma_cli/ # Python 包目录
|
|
248
|
-
├── __init__.py
|
|
249
|
-
├── cli.py # 命令行入口
|
|
250
|
-
├── client.py # API 客户端
|
|
251
|
-
├── state.py # 状态管理
|
|
252
|
-
├── resources.py # 资源查询
|
|
253
|
-
├── submit.py # 任务提交
|
|
254
|
-
└── calc.py # 默认任务脚本
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
## 不在范围
|
|
258
|
-
|
|
259
|
-
- 不做多用户 / 不改压测脚本
|
|
260
|
-
- 不做分布式压测
|
|
@@ -4,13 +4,12 @@ base_url: https://szc.test.dp.tech
|
|
|
4
4
|
|
|
5
5
|
auth:
|
|
6
6
|
email: "" # 验证码登录邮箱(留空则必须填 token)
|
|
7
|
-
captcha: "
|
|
7
|
+
captcha: "" # 留空,执行 dma login 时交互输入验证码
|
|
8
8
|
token: "" # 直接配 token(优先于 email 登录,留空走 email)
|
|
9
9
|
uexid: "" # 留空则从 JWT 自动解析为 fe_<userId>
|
|
10
10
|
user_id: "" # 留空则从 JWT 自动解析
|
|
11
11
|
|
|
12
12
|
defaults:
|
|
13
|
-
biz_platform: 2
|
|
14
13
|
name_prefix: "cli-job"
|
|
15
14
|
cmd: "python calc.py"
|
|
16
15
|
max_run_time: 15
|
|
@@ -24,11 +24,12 @@ _PKG_DIR = Path(__file__).parent
|
|
|
24
24
|
CONFIG_FILE = Path.cwd() / "config.yaml" if (Path.cwd() / "config.yaml").exists() else _PKG_DIR / "config.yaml"
|
|
25
25
|
EXAMPLE_CONFIG = _PKG_DIR / "config.example.yaml"
|
|
26
26
|
|
|
27
|
+
BIZ_PLATFORM = 2
|
|
28
|
+
|
|
27
29
|
DEFAULT_CONFIG = {
|
|
28
30
|
"base_url": "https://szc.test.dp.tech",
|
|
29
|
-
"auth": {"email": "", "captcha": "
|
|
31
|
+
"auth": {"email": "", "captcha": "", "token": "", "uexid": "", "user_id": ""},
|
|
30
32
|
"defaults": {
|
|
31
|
-
"biz_platform": 2,
|
|
32
33
|
"name_prefix": "cli-job",
|
|
33
34
|
"cmd": "python calc.py",
|
|
34
35
|
"max_run_time": 15,
|
|
@@ -153,7 +154,14 @@ class DmaClient:
|
|
|
153
154
|
email = email or self.config["auth"].get("email")
|
|
154
155
|
if not email:
|
|
155
156
|
raise RuntimeError("未配置 auth.email,无法验证码登录(请在 config.yaml 填写或改用 token)")
|
|
156
|
-
captcha = self.config["auth"].get("captcha") or "
|
|
157
|
+
captcha = str(self.config["auth"].get("captcha") or "").strip()
|
|
158
|
+
if not captcha:
|
|
159
|
+
try:
|
|
160
|
+
captcha = input("请输入邮箱验证码: ").strip()
|
|
161
|
+
except (EOFError, KeyboardInterrupt):
|
|
162
|
+
raise RuntimeError("已取消验证码输入,未发送登录请求") from None
|
|
163
|
+
if not captcha:
|
|
164
|
+
raise RuntimeError("验证码不能为空,请重新执行 dma login")
|
|
157
165
|
url = self.base_url + LOGIN_PATH
|
|
158
166
|
payload = {
|
|
159
167
|
"channel": "gkx-talent-universe",
|
|
@@ -12,14 +12,10 @@ import sys
|
|
|
12
12
|
|
|
13
13
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
14
14
|
|
|
15
|
-
from .client import STATUS_MAP
|
|
15
|
+
from .client import BIZ_PLATFORM, STATUS_MAP
|
|
16
16
|
from .state import set_current
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
def _biz_platform(client) -> int:
|
|
20
|
-
return int(client.defaults.get("biz_platform", 2))
|
|
21
|
-
|
|
22
|
-
|
|
23
19
|
def _print_table(headers, rows) -> None:
|
|
24
20
|
"""简易对齐表格打印。"""
|
|
25
21
|
if not rows:
|
|
@@ -38,7 +34,7 @@ def _print_table(headers, rows) -> None:
|
|
|
38
34
|
|
|
39
35
|
# ============ project ============
|
|
40
36
|
def project_list(client) -> list:
|
|
41
|
-
params = {"queryType": "all", "pageSize": 1000, "bizPlatform":
|
|
37
|
+
params = {"queryType": "all", "pageSize": 1000, "bizPlatform": BIZ_PLATFORM, "page": 1}
|
|
42
38
|
resp = client.get("/dmaapi/szc/v2/project/list", params=params)
|
|
43
39
|
data = (resp or {}).get("data") or {}
|
|
44
40
|
return data.get("items") or data.get("list") or []
|
|
@@ -123,7 +119,7 @@ def handle_sku_use(client, args) -> None:
|
|
|
123
119
|
|
|
124
120
|
# ============ image (软件目录) ============
|
|
125
121
|
def software_list(client) -> list:
|
|
126
|
-
resp = client.get("/dmaapi/szc/v1/softwares/deep-mining", params={"bizPlatform":
|
|
122
|
+
resp = client.get("/dmaapi/szc/v1/softwares/deep-mining", params={"bizPlatform": BIZ_PLATFORM})
|
|
127
123
|
return ((resp or {}).get("data") or {}).get("items") or []
|
|
128
124
|
|
|
129
125
|
|
|
@@ -155,7 +151,7 @@ def handle_image_list(client, args) -> None:
|
|
|
155
151
|
all_images = []
|
|
156
152
|
|
|
157
153
|
# 1. 查询软件目录(/softwares/deep-mining)
|
|
158
|
-
resp = client.get("/dmaapi/szc/v1/softwares/deep-mining", params={"bizPlatform":
|
|
154
|
+
resp = client.get("/dmaapi/szc/v1/softwares/deep-mining", params={"bizPlatform": BIZ_PLATFORM})
|
|
159
155
|
if resp.get("code") == 0:
|
|
160
156
|
for item in resp.get("data", {}).get("items", []):
|
|
161
157
|
for version in item.get("versions", []):
|
|
@@ -170,7 +166,7 @@ def handle_image_list(client, args) -> None:
|
|
|
170
166
|
|
|
171
167
|
# 2. 查询公共镜像(/image/public)
|
|
172
168
|
resp = client.get("/dmaapi/szc/v1/image/public/deep-mining",
|
|
173
|
-
params={"page": 1, "pageSize": 1000, "bizPlatform":
|
|
169
|
+
params={"page": 1, "pageSize": 1000, "bizPlatform": BIZ_PLATFORM})
|
|
174
170
|
if resp.get("code") == 0:
|
|
175
171
|
for item in resp.get("data", {}).get("items", []):
|
|
176
172
|
# url 字段可能是 img
|
|
@@ -184,7 +180,7 @@ def handle_image_list(client, args) -> None:
|
|
|
184
180
|
|
|
185
181
|
# 3. 查询私有镜像(/image/private)
|
|
186
182
|
resp = client.get("/dmaapi/szc/v1/image/private",
|
|
187
|
-
params={"type": "image", "device": "container", "page": 1, "pageSize": 50, "bizPlatform":
|
|
183
|
+
params={"type": "image", "device": "container", "page": 1, "pageSize": 50, "bizPlatform": BIZ_PLATFORM})
|
|
188
184
|
if resp.get("code") == 0:
|
|
189
185
|
for item in resp.get("data", {}).get("items", []):
|
|
190
186
|
url = item.get("url") or ""
|
|
@@ -24,7 +24,7 @@ import requests
|
|
|
24
24
|
|
|
25
25
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
26
26
|
|
|
27
|
-
from .client import DmaClient, STATUS_MAP
|
|
27
|
+
from .client import BIZ_PLATFORM, DmaClient, STATUS_MAP
|
|
28
28
|
|
|
29
29
|
# 内置默认任务脚本(无 --input_directory 时上传它)
|
|
30
30
|
DEFAULT_INPUT = Path(__file__).parent / "calc.py"
|
|
@@ -57,7 +57,7 @@ def find_software_by_image_address(client: DmaClient, image_address: str) -> dic
|
|
|
57
57
|
2. softwareVersion 字段精确匹配
|
|
58
58
|
3. softwareName 模糊匹配
|
|
59
59
|
"""
|
|
60
|
-
resp = client.get("/dmaapi/szc/v1/softwares/deep-mining", params={"bizPlatform":
|
|
60
|
+
resp = client.get("/dmaapi/szc/v1/softwares/deep-mining", params={"bizPlatform": BIZ_PLATFORM})
|
|
61
61
|
if resp.get("code") != 0:
|
|
62
62
|
raise RuntimeError(f"查询软件目录失败: code={resp.get('code')}")
|
|
63
63
|
|
|
@@ -407,7 +407,7 @@ def handle_submit(client, args):
|
|
|
407
407
|
json_config = json_config["_batch_data"][0] if json_config["_batch_data"] else {}
|
|
408
408
|
|
|
409
409
|
# 映射 JSON 字段到 overrides
|
|
410
|
-
if json_config.get("project_id"):
|
|
410
|
+
if json_config.get("project_id") is not None and json_config.get("project_id") != 0:
|
|
411
411
|
overrides["project_id"] = json_config["project_id"]
|
|
412
412
|
if json_config.get("job_name"):
|
|
413
413
|
args.job_name = json_config["job_name"]
|
|
@@ -435,7 +435,7 @@ def handle_submit(client, args):
|
|
|
435
435
|
overrides["dataset_path"] = json_config["dataset_path"]
|
|
436
436
|
|
|
437
437
|
# CLI 参数覆盖 JSON 配置
|
|
438
|
-
if args.project_id:
|
|
438
|
+
if args.project_id is not None and args.project_id != 0:
|
|
439
439
|
overrides["project_id"] = args.project_id
|
|
440
440
|
if args.machine_type:
|
|
441
441
|
overrides["machine_type"] = args.machine_type
|
|
@@ -542,8 +542,12 @@ def handle_submit(client, args):
|
|
|
542
542
|
for key in ["project_id", "job_name", "machine_type", "image_address", "command",
|
|
543
543
|
"input_directory", "max_run_time", "max_reschedule_times", "nnode",
|
|
544
544
|
"log_file", "out_files", "result_path", "dataset_path"]:
|
|
545
|
-
|
|
546
|
-
|
|
545
|
+
val = config.get(key)
|
|
546
|
+
if val is not None:
|
|
547
|
+
# 如果 project_id 显式为 0,则应使用默认当前项目
|
|
548
|
+
if key == "project_id" and val == 0:
|
|
549
|
+
continue
|
|
550
|
+
item_overrides[key] = val
|
|
547
551
|
|
|
548
552
|
# 自动查找
|
|
549
553
|
if item_overrides.get("machine_type"):
|
|
@@ -670,4 +674,4 @@ def handle_submit(client, args):
|
|
|
670
674
|
print("📊 轮询任务状态...")
|
|
671
675
|
poll_task_status(client, result['job_id'])
|
|
672
676
|
else:
|
|
673
|
-
print(f"❌ 提交失败: {result['error']}")
|
|
677
|
+
print(f"❌ 提交失败: {result['error']}")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dma-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: DMA 任务提交命令行工具
|
|
5
5
|
Requires-Python: >=3.8
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -9,11 +9,7 @@ Requires-Dist: pyyaml
|
|
|
9
9
|
|
|
10
10
|
# DMA CLI —— 任务提交工具
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
与压测无关,只借用压测脚本里已验证的接口形态,不改动任何压测内容。
|
|
14
|
-
|
|
15
|
-
> **术语**:本工具的 `image`(镜像)即网页上的「软件目录」,底层接口为 `/softwares/deep-mining`,
|
|
16
|
-
> snapshot 用 `softwareId`/`softwareVersionId`。容器镜像接口(image/public|private)不参与 job 提交,但 `image list` 合并展示供参考。
|
|
12
|
+
深度挖掘算法(DMA)网页外的命令行提交工具,用于提交单个或批量任务。
|
|
17
13
|
|
|
18
14
|
## 安装
|
|
19
15
|
|
|
@@ -22,14 +18,6 @@ Requires-Dist: pyyaml
|
|
|
22
18
|
pip install dma-cli
|
|
23
19
|
dma --help
|
|
24
20
|
|
|
25
|
-
# 从源码安装
|
|
26
|
-
cd tools/dma-cli
|
|
27
|
-
pip install .
|
|
28
|
-
|
|
29
|
-
# 方式二:本地开发模式(可直接修改代码调试)
|
|
30
|
-
cd tools/dma-cli
|
|
31
|
-
pip install -e .
|
|
32
|
-
|
|
33
21
|
# 安装后 dma 命令即可全局使用
|
|
34
22
|
dma --help
|
|
35
23
|
```
|
|
@@ -64,12 +52,11 @@ cp config.example.yaml config.yaml
|
|
|
64
52
|
base_url: https://szc.test.dp.tech
|
|
65
53
|
auth:
|
|
66
54
|
email: "" # 验证码登录邮箱(留空则必须填 token)
|
|
67
|
-
captcha: "
|
|
55
|
+
captcha: "" # 留空,执行 dma login 时交互输入验证码
|
|
68
56
|
token: "" # 直接配 token(优先于 email 登录)
|
|
69
57
|
uexid: "" # 留空从 JWT 自动解析为 fe_<userId>
|
|
70
58
|
user_id: "" # 留空从 JWT 自动解析
|
|
71
59
|
defaults:
|
|
72
|
-
biz_platform: 2
|
|
73
60
|
name_prefix: "cli-job"
|
|
74
61
|
max_run_time: 15
|
|
75
62
|
max_run_time_unit: d
|
|
@@ -108,6 +95,11 @@ dma login
|
|
|
108
95
|
dma login --email you@example.com
|
|
109
96
|
```
|
|
110
97
|
|
|
98
|
+
`auth.captcha` 留空时,登录会提示 `请输入邮箱验证码:`,输入本次验证码后提交。
|
|
99
|
+
验证码不会保存到配置文件或登录缓存。若配置中已填写 `auth.captcha`,则使用该值;
|
|
100
|
+
已有配置中的旧验证码请清空,以启用交互输入。输入为空或取消输入时,不发送登录请求。
|
|
101
|
+
此命令只校验输入的验证码,不会自动发送验证码邮件。
|
|
102
|
+
|
|
111
103
|
解析 JWT 缓存 `token` / `user_id` / `uexid` 到 `~/.dma_cli/credentials.json`。也可跳过登录,直接在 `config.yaml` 填 `token`。
|
|
112
104
|
|
|
113
105
|
### whoami —— 查看当前状态
|
|
@@ -120,12 +112,19 @@ dma whoami
|
|
|
120
112
|
|
|
121
113
|
### project —— 项目管理
|
|
122
114
|
|
|
115
|
+
先查询当前账号可访问的项目,根据项目名称找到自己的 `projectId`,再设为当前项目:
|
|
116
|
+
|
|
123
117
|
```bash
|
|
124
|
-
dma project list
|
|
125
|
-
dma project use
|
|
126
|
-
dma project current
|
|
118
|
+
dma project list # 列出 projectId | name
|
|
119
|
+
dma project use <你的项目ID> # 替换为上一步查到的 projectId
|
|
120
|
+
dma project current # 确认当前项目 ID 和名称
|
|
127
121
|
```
|
|
128
122
|
|
|
123
|
+
`<你的项目ID>` 是占位符,执行前请替换为列表中你要使用的数字 ID,不要输入尖括号。
|
|
124
|
+
当前项目会保存在本机,后续提交任务时省略 `--project_id` 或设置为 `0` 即使用该项目;
|
|
125
|
+
JSON 配置中的 `"project_id": 0` 含义相同。`0` 表示使用当前项目,不是真实项目 ID,首次提交前需先完成项目选择。
|
|
126
|
+
如需单次提交到其他项目,可传入 `--project_id <你的项目ID>`,不会改变已保存的当前项目。
|
|
127
|
+
|
|
129
128
|
### sku —— 机型查询
|
|
130
129
|
|
|
131
130
|
```bash
|
|
@@ -158,7 +157,7 @@ dma job submit -i job.example.json
|
|
|
158
157
|
dma job submit \
|
|
159
158
|
--machine_type c2_m2_cpu \
|
|
160
159
|
--image_address ubuntu:22.04-py3.10-terminal-20260916 \
|
|
161
|
-
--project_id
|
|
160
|
+
--project_id 0 \
|
|
162
161
|
--command "python calc.py" \
|
|
163
162
|
--job_name my-job
|
|
164
163
|
```
|
|
@@ -175,7 +174,6 @@ dma job submit \
|
|
|
175
174
|
| `--machine_type` | `-t` | 机器类型名(自动匹配) |
|
|
176
175
|
| `--command` | `-c` | 执行命令 |
|
|
177
176
|
| `--max_run_time` | — | 最大运行时间(分钟) |
|
|
178
|
-
| `--nnode` | — | 节点数 |
|
|
179
177
|
| `--count` | — | 批量提交任务数 |
|
|
180
178
|
| `--concurrency` | — | 批量并发数 |
|
|
181
179
|
| `--batch_file` | — | 批量任务 JSON 数组文件 |
|
|
@@ -191,7 +189,8 @@ dma job submit \
|
|
|
191
189
|
dma job submit \
|
|
192
190
|
--machine_type c2_m2_cpu \
|
|
193
191
|
--image_address ubuntu:22.04-py3.10-terminal-20260916 \
|
|
194
|
-
--
|
|
192
|
+
--command "python calc.py" \
|
|
193
|
+
--count 5 \
|
|
195
194
|
--concurrency 5
|
|
196
195
|
```
|
|
197
196
|
|
|
@@ -204,7 +203,7 @@ dma job submit \
|
|
|
204
203
|
# {"job_name": "job-002", "machine_type": "c4_m8_cpu", ...}
|
|
205
204
|
# ]
|
|
206
205
|
|
|
207
|
-
dma job submit --batch_file batch.json --concurrency 3
|
|
206
|
+
dma job submit --batch_file batch.example.json --concurrency 3
|
|
208
207
|
```
|
|
209
208
|
|
|
210
209
|
### 方式三:--batch_input_dir(目录扫描)
|
|
@@ -212,9 +211,10 @@ dma job submit --batch_file batch.json --concurrency 3
|
|
|
212
211
|
```bash
|
|
213
212
|
# 每个子目录作为一个任务的输入
|
|
214
213
|
dma job submit \
|
|
215
|
-
--batch_input_dir ./
|
|
214
|
+
--batch_input_dir ./input \
|
|
216
215
|
--machine_type c2_m2_cpu \
|
|
217
216
|
--image_address ubuntu:22.04-py3.10-terminal-20260916 \
|
|
217
|
+
--command "python calc.py" \
|
|
218
218
|
--concurrency 5
|
|
219
219
|
```
|
|
220
220
|
|
|
@@ -228,9 +228,10 @@ cp config.example.yaml config.yaml
|
|
|
228
228
|
# 2. 查看状态
|
|
229
229
|
dma whoami
|
|
230
230
|
|
|
231
|
-
# 3.
|
|
231
|
+
# 3. 查询并选择自己的项目(将占位符替换为列表中的 projectId,不保留尖括号)
|
|
232
232
|
dma project list
|
|
233
|
-
dma project use
|
|
233
|
+
dma project use <你的项目ID>
|
|
234
|
+
dma project current
|
|
234
235
|
|
|
235
236
|
# 4. 查询机型和镜像
|
|
236
237
|
dma sku list --kind cpu
|
|
@@ -240,30 +241,5 @@ dma image list
|
|
|
240
241
|
dma job submit -i job.example.json
|
|
241
242
|
|
|
242
243
|
# 6. 批量提交
|
|
243
|
-
dma job submit --batch_file batch.json
|
|
244
|
-
```
|
|
245
|
-
|
|
246
|
-
## 文件结构
|
|
247
|
-
|
|
244
|
+
dma job submit --batch_file batch.example.json
|
|
248
245
|
```
|
|
249
|
-
tools/dma-cli/
|
|
250
|
-
├── README.md # 本文档
|
|
251
|
-
├── pyproject.toml # 打包配置
|
|
252
|
-
├── requirements.txt # requests, pyyaml
|
|
253
|
-
├── config.example.yaml # 配置模板
|
|
254
|
-
├── job.example.json # 任务配置示例
|
|
255
|
-
├── batch.example.json # 批量任务示例
|
|
256
|
-
└── dma_cli/ # Python 包目录
|
|
257
|
-
├── __init__.py
|
|
258
|
-
├── cli.py # 命令行入口
|
|
259
|
-
├── client.py # API 客户端
|
|
260
|
-
├── state.py # 状态管理
|
|
261
|
-
├── resources.py # 资源查询
|
|
262
|
-
├── submit.py # 任务提交
|
|
263
|
-
└── calc.py # 默认任务脚本
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
## 不在范围
|
|
267
|
-
|
|
268
|
-
- 不做多用户 / 不改压测脚本
|
|
269
|
-
- 不做分布式压测
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
from unittest.mock import Mock
|
|
3
|
+
|
|
4
|
+
from dma_cli.resources import project_list, software_list
|
|
5
|
+
from dma_cli.submit import find_software_by_image_address
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class BizPlatformTests(unittest.TestCase):
|
|
9
|
+
def test_requests_ignore_legacy_platform_config(self):
|
|
10
|
+
for operation, args in (
|
|
11
|
+
(project_list, ()),
|
|
12
|
+
(software_list, ()),
|
|
13
|
+
(find_software_by_image_address, ("test-image",)),
|
|
14
|
+
):
|
|
15
|
+
with self.subTest(operation=operation.__name__):
|
|
16
|
+
client = Mock()
|
|
17
|
+
client.defaults = {"biz_platform": 99}
|
|
18
|
+
client.get.return_value = {"code": 0, "data": {"items": []}}
|
|
19
|
+
try:
|
|
20
|
+
operation(client, *args)
|
|
21
|
+
except RuntimeError as error:
|
|
22
|
+
self.assertIn("未找到镜像", str(error))
|
|
23
|
+
self.assertEqual(client.get.call_args.kwargs["params"]["bizPlatform"], 2)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
if __name__ == "__main__":
|
|
27
|
+
unittest.main()
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
from unittest.mock import Mock, patch
|
|
3
|
+
|
|
4
|
+
from dma_cli.client import DmaClient
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class LoginCaptchaTests(unittest.TestCase):
|
|
8
|
+
def setUp(self):
|
|
9
|
+
save = patch("dma_cli.client.save_creds")
|
|
10
|
+
save.start()
|
|
11
|
+
self.addCleanup(save.stop)
|
|
12
|
+
self.client = DmaClient.__new__(DmaClient)
|
|
13
|
+
self.client.config = {
|
|
14
|
+
"base_url": "https://example.invalid",
|
|
15
|
+
"auth": {"email": "user@example.invalid", "captcha": ""},
|
|
16
|
+
}
|
|
17
|
+
self.client._http = Mock()
|
|
18
|
+
self.client._http.post.return_value.json.return_value = {
|
|
19
|
+
"code": 0, "data": {"token": "test-token"}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
def test_prompts_for_missing_captcha_and_sends_input(self):
|
|
23
|
+
with patch("builtins.input", return_value=" 123456 ") as prompt, \
|
|
24
|
+
patch("dma_cli.client.save_creds"):
|
|
25
|
+
self.client.login()
|
|
26
|
+
prompt.assert_called_once()
|
|
27
|
+
payload = self.client._http.post.call_args.kwargs["json"]
|
|
28
|
+
self.assertEqual(payload["emailCode"], "123456")
|
|
29
|
+
self.assertEqual(payload["code"], "123456")
|
|
30
|
+
self.assertEqual(self.client.config["auth"]["captcha"], "")
|
|
31
|
+
self.assertNotIn("captcha", self.client.creds)
|
|
32
|
+
|
|
33
|
+
def test_configured_captcha_does_not_prompt(self):
|
|
34
|
+
self.client.config["auth"]["captcha"] = "654321"
|
|
35
|
+
with patch("builtins.input") as prompt, patch("dma_cli.client.save_creds"):
|
|
36
|
+
self.client.login()
|
|
37
|
+
prompt.assert_not_called()
|
|
38
|
+
self.assertEqual(self.client._http.post.call_args.kwargs["json"]["code"], "654321")
|
|
39
|
+
|
|
40
|
+
def test_empty_input_does_not_send_login_request(self):
|
|
41
|
+
with patch("builtins.input", return_value=" "):
|
|
42
|
+
with self.assertRaisesRegex(RuntimeError, "验证码不能为空"):
|
|
43
|
+
self.client.login()
|
|
44
|
+
self.client._http.post.assert_not_called()
|
|
45
|
+
|
|
46
|
+
def test_cancelled_input_does_not_send_login_request(self):
|
|
47
|
+
for error in (EOFError, KeyboardInterrupt):
|
|
48
|
+
with self.subTest(error=error), patch("builtins.input", side_effect=error):
|
|
49
|
+
with self.assertRaisesRegex(RuntimeError, "取消验证码输入"):
|
|
50
|
+
self.client.login()
|
|
51
|
+
self.client._http.post.assert_not_called()
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
if __name__ == "__main__":
|
|
55
|
+
unittest.main()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|