dma-cli 0.1.1__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.1 → dma_cli-0.1.2}/PKG-INFO +24 -12
- {dma_cli-0.1.1 → dma_cli-0.1.2}/README.md +23 -11
- {dma_cli-0.1.1 → dma_cli-0.1.2}/config.example.yaml +1 -2
- {dma_cli-0.1.1 → dma_cli-0.1.2}/dma_cli/__init__.py +1 -1
- {dma_cli-0.1.1 → dma_cli-0.1.2}/dma_cli/client.py +11 -3
- {dma_cli-0.1.1 → dma_cli-0.1.2}/dma_cli/resources.py +6 -10
- {dma_cli-0.1.1 → dma_cli-0.1.2}/dma_cli/submit.py +3 -3
- {dma_cli-0.1.1 → dma_cli-0.1.2}/dma_cli.egg-info/PKG-INFO +24 -12
- {dma_cli-0.1.1 → dma_cli-0.1.2}/dma_cli.egg-info/SOURCES.txt +3 -1
- {dma_cli-0.1.1 → 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.1 → dma_cli-0.1.2}/batch.example.json +0 -0
- {dma_cli-0.1.1 → dma_cli-0.1.2}/dma_cli/__main__.py +0 -0
- {dma_cli-0.1.1 → dma_cli-0.1.2}/dma_cli/calc.py +0 -0
- {dma_cli-0.1.1 → dma_cli-0.1.2}/dma_cli/cli.py +0 -0
- {dma_cli-0.1.1 → dma_cli-0.1.2}/dma_cli/state.py +0 -0
- {dma_cli-0.1.1 → dma_cli-0.1.2}/dma_cli.egg-info/dependency_links.txt +0 -0
- {dma_cli-0.1.1 → dma_cli-0.1.2}/dma_cli.egg-info/entry_points.txt +0 -0
- {dma_cli-0.1.1 → dma_cli-0.1.2}/dma_cli.egg-info/requires.txt +0 -0
- {dma_cli-0.1.1 → dma_cli-0.1.2}/dma_cli.egg-info/top_level.txt +0 -0
- {dma_cli-0.1.1 → dma_cli-0.1.2}/input/calc.py +0 -0
- {dma_cli-0.1.1 → dma_cli-0.1.2}/input/case1/calc.py +0 -0
- {dma_cli-0.1.1 → dma_cli-0.1.2}/input/case2/calc.py +0 -0
- {dma_cli-0.1.1 → dma_cli-0.1.2}/job.example.json +0 -0
- {dma_cli-0.1.1 → 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
|
|
@@ -52,12 +52,11 @@ cp config.example.yaml config.yaml
|
|
|
52
52
|
base_url: https://szc.test.dp.tech
|
|
53
53
|
auth:
|
|
54
54
|
email: "" # 验证码登录邮箱(留空则必须填 token)
|
|
55
|
-
captcha: "
|
|
55
|
+
captcha: "" # 留空,执行 dma login 时交互输入验证码
|
|
56
56
|
token: "" # 直接配 token(优先于 email 登录)
|
|
57
57
|
uexid: "" # 留空从 JWT 自动解析为 fe_<userId>
|
|
58
58
|
user_id: "" # 留空从 JWT 自动解析
|
|
59
59
|
defaults:
|
|
60
|
-
biz_platform: 2
|
|
61
60
|
name_prefix: "cli-job"
|
|
62
61
|
max_run_time: 15
|
|
63
62
|
max_run_time_unit: d
|
|
@@ -96,6 +95,11 @@ dma login
|
|
|
96
95
|
dma login --email you@example.com
|
|
97
96
|
```
|
|
98
97
|
|
|
98
|
+
`auth.captcha` 留空时,登录会提示 `请输入邮箱验证码:`,输入本次验证码后提交。
|
|
99
|
+
验证码不会保存到配置文件或登录缓存。若配置中已填写 `auth.captcha`,则使用该值;
|
|
100
|
+
已有配置中的旧验证码请清空,以启用交互输入。输入为空或取消输入时,不发送登录请求。
|
|
101
|
+
此命令只校验输入的验证码,不会自动发送验证码邮件。
|
|
102
|
+
|
|
99
103
|
解析 JWT 缓存 `token` / `user_id` / `uexid` 到 `~/.dma_cli/credentials.json`。也可跳过登录,直接在 `config.yaml` 填 `token`。
|
|
100
104
|
|
|
101
105
|
### whoami —— 查看当前状态
|
|
@@ -108,12 +112,19 @@ dma whoami
|
|
|
108
112
|
|
|
109
113
|
### project —— 项目管理
|
|
110
114
|
|
|
115
|
+
先查询当前账号可访问的项目,根据项目名称找到自己的 `projectId`,再设为当前项目:
|
|
116
|
+
|
|
111
117
|
```bash
|
|
112
|
-
dma project list
|
|
113
|
-
dma project use
|
|
114
|
-
dma project current
|
|
118
|
+
dma project list # 列出 projectId | name
|
|
119
|
+
dma project use <你的项目ID> # 替换为上一步查到的 projectId
|
|
120
|
+
dma project current # 确认当前项目 ID 和名称
|
|
115
121
|
```
|
|
116
122
|
|
|
123
|
+
`<你的项目ID>` 是占位符,执行前请替换为列表中你要使用的数字 ID,不要输入尖括号。
|
|
124
|
+
当前项目会保存在本机,后续提交任务时省略 `--project_id` 或设置为 `0` 即使用该项目;
|
|
125
|
+
JSON 配置中的 `"project_id": 0` 含义相同。`0` 表示使用当前项目,不是真实项目 ID,首次提交前需先完成项目选择。
|
|
126
|
+
如需单次提交到其他项目,可传入 `--project_id <你的项目ID>`,不会改变已保存的当前项目。
|
|
127
|
+
|
|
117
128
|
### sku —— 机型查询
|
|
118
129
|
|
|
119
130
|
```bash
|
|
@@ -146,7 +157,7 @@ dma job submit -i job.example.json
|
|
|
146
157
|
dma job submit \
|
|
147
158
|
--machine_type c2_m2_cpu \
|
|
148
159
|
--image_address ubuntu:22.04-py3.10-terminal-20260916 \
|
|
149
|
-
--project_id
|
|
160
|
+
--project_id 0 \
|
|
150
161
|
--command "python calc.py" \
|
|
151
162
|
--job_name my-job
|
|
152
163
|
```
|
|
@@ -192,7 +203,7 @@ dma job submit \
|
|
|
192
203
|
# {"job_name": "job-002", "machine_type": "c4_m8_cpu", ...}
|
|
193
204
|
# ]
|
|
194
205
|
|
|
195
|
-
dma job submit --batch_file batch.json --concurrency 3
|
|
206
|
+
dma job submit --batch_file batch.example.json --concurrency 3
|
|
196
207
|
```
|
|
197
208
|
|
|
198
209
|
### 方式三:--batch_input_dir(目录扫描)
|
|
@@ -200,7 +211,7 @@ dma job submit --batch_file batch.json --concurrency 3
|
|
|
200
211
|
```bash
|
|
201
212
|
# 每个子目录作为一个任务的输入
|
|
202
213
|
dma job submit \
|
|
203
|
-
--batch_input_dir ./
|
|
214
|
+
--batch_input_dir ./input \
|
|
204
215
|
--machine_type c2_m2_cpu \
|
|
205
216
|
--image_address ubuntu:22.04-py3.10-terminal-20260916 \
|
|
206
217
|
--command "python calc.py" \
|
|
@@ -217,9 +228,10 @@ cp config.example.yaml config.yaml
|
|
|
217
228
|
# 2. 查看状态
|
|
218
229
|
dma whoami
|
|
219
230
|
|
|
220
|
-
# 3.
|
|
231
|
+
# 3. 查询并选择自己的项目(将占位符替换为列表中的 projectId,不保留尖括号)
|
|
221
232
|
dma project list
|
|
222
|
-
dma project use
|
|
233
|
+
dma project use <你的项目ID>
|
|
234
|
+
dma project current
|
|
223
235
|
|
|
224
236
|
# 4. 查询机型和镜像
|
|
225
237
|
dma sku list --kind cpu
|
|
@@ -229,5 +241,5 @@ dma image list
|
|
|
229
241
|
dma job submit -i job.example.json
|
|
230
242
|
|
|
231
243
|
# 6. 批量提交
|
|
232
|
-
dma job submit --batch_file batch.json
|
|
244
|
+
dma job submit --batch_file batch.example.json
|
|
233
245
|
```
|
|
@@ -43,12 +43,11 @@ cp config.example.yaml config.yaml
|
|
|
43
43
|
base_url: https://szc.test.dp.tech
|
|
44
44
|
auth:
|
|
45
45
|
email: "" # 验证码登录邮箱(留空则必须填 token)
|
|
46
|
-
captcha: "
|
|
46
|
+
captcha: "" # 留空,执行 dma login 时交互输入验证码
|
|
47
47
|
token: "" # 直接配 token(优先于 email 登录)
|
|
48
48
|
uexid: "" # 留空从 JWT 自动解析为 fe_<userId>
|
|
49
49
|
user_id: "" # 留空从 JWT 自动解析
|
|
50
50
|
defaults:
|
|
51
|
-
biz_platform: 2
|
|
52
51
|
name_prefix: "cli-job"
|
|
53
52
|
max_run_time: 15
|
|
54
53
|
max_run_time_unit: d
|
|
@@ -87,6 +86,11 @@ dma login
|
|
|
87
86
|
dma login --email you@example.com
|
|
88
87
|
```
|
|
89
88
|
|
|
89
|
+
`auth.captcha` 留空时,登录会提示 `请输入邮箱验证码:`,输入本次验证码后提交。
|
|
90
|
+
验证码不会保存到配置文件或登录缓存。若配置中已填写 `auth.captcha`,则使用该值;
|
|
91
|
+
已有配置中的旧验证码请清空,以启用交互输入。输入为空或取消输入时,不发送登录请求。
|
|
92
|
+
此命令只校验输入的验证码,不会自动发送验证码邮件。
|
|
93
|
+
|
|
90
94
|
解析 JWT 缓存 `token` / `user_id` / `uexid` 到 `~/.dma_cli/credentials.json`。也可跳过登录,直接在 `config.yaml` 填 `token`。
|
|
91
95
|
|
|
92
96
|
### whoami —— 查看当前状态
|
|
@@ -99,12 +103,19 @@ dma whoami
|
|
|
99
103
|
|
|
100
104
|
### project —— 项目管理
|
|
101
105
|
|
|
106
|
+
先查询当前账号可访问的项目,根据项目名称找到自己的 `projectId`,再设为当前项目:
|
|
107
|
+
|
|
102
108
|
```bash
|
|
103
|
-
dma project list
|
|
104
|
-
dma project use
|
|
105
|
-
dma project current
|
|
109
|
+
dma project list # 列出 projectId | name
|
|
110
|
+
dma project use <你的项目ID> # 替换为上一步查到的 projectId
|
|
111
|
+
dma project current # 确认当前项目 ID 和名称
|
|
106
112
|
```
|
|
107
113
|
|
|
114
|
+
`<你的项目ID>` 是占位符,执行前请替换为列表中你要使用的数字 ID,不要输入尖括号。
|
|
115
|
+
当前项目会保存在本机,后续提交任务时省略 `--project_id` 或设置为 `0` 即使用该项目;
|
|
116
|
+
JSON 配置中的 `"project_id": 0` 含义相同。`0` 表示使用当前项目,不是真实项目 ID,首次提交前需先完成项目选择。
|
|
117
|
+
如需单次提交到其他项目,可传入 `--project_id <你的项目ID>`,不会改变已保存的当前项目。
|
|
118
|
+
|
|
108
119
|
### sku —— 机型查询
|
|
109
120
|
|
|
110
121
|
```bash
|
|
@@ -137,7 +148,7 @@ dma job submit -i job.example.json
|
|
|
137
148
|
dma job submit \
|
|
138
149
|
--machine_type c2_m2_cpu \
|
|
139
150
|
--image_address ubuntu:22.04-py3.10-terminal-20260916 \
|
|
140
|
-
--project_id
|
|
151
|
+
--project_id 0 \
|
|
141
152
|
--command "python calc.py" \
|
|
142
153
|
--job_name my-job
|
|
143
154
|
```
|
|
@@ -183,7 +194,7 @@ dma job submit \
|
|
|
183
194
|
# {"job_name": "job-002", "machine_type": "c4_m8_cpu", ...}
|
|
184
195
|
# ]
|
|
185
196
|
|
|
186
|
-
dma job submit --batch_file batch.json --concurrency 3
|
|
197
|
+
dma job submit --batch_file batch.example.json --concurrency 3
|
|
187
198
|
```
|
|
188
199
|
|
|
189
200
|
### 方式三:--batch_input_dir(目录扫描)
|
|
@@ -191,7 +202,7 @@ dma job submit --batch_file batch.json --concurrency 3
|
|
|
191
202
|
```bash
|
|
192
203
|
# 每个子目录作为一个任务的输入
|
|
193
204
|
dma job submit \
|
|
194
|
-
--batch_input_dir ./
|
|
205
|
+
--batch_input_dir ./input \
|
|
195
206
|
--machine_type c2_m2_cpu \
|
|
196
207
|
--image_address ubuntu:22.04-py3.10-terminal-20260916 \
|
|
197
208
|
--command "python calc.py" \
|
|
@@ -208,9 +219,10 @@ cp config.example.yaml config.yaml
|
|
|
208
219
|
# 2. 查看状态
|
|
209
220
|
dma whoami
|
|
210
221
|
|
|
211
|
-
# 3.
|
|
222
|
+
# 3. 查询并选择自己的项目(将占位符替换为列表中的 projectId,不保留尖括号)
|
|
212
223
|
dma project list
|
|
213
|
-
dma project use
|
|
224
|
+
dma project use <你的项目ID>
|
|
225
|
+
dma project current
|
|
214
226
|
|
|
215
227
|
# 4. 查询机型和镜像
|
|
216
228
|
dma sku list --kind cpu
|
|
@@ -220,5 +232,5 @@ dma image list
|
|
|
220
232
|
dma job submit -i job.example.json
|
|
221
233
|
|
|
222
234
|
# 6. 批量提交
|
|
223
|
-
dma job submit --batch_file batch.json
|
|
235
|
+
dma job submit --batch_file batch.example.json
|
|
224
236
|
```
|
|
@@ -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
|
|
|
@@ -674,4 +674,4 @@ def handle_submit(client, args):
|
|
|
674
674
|
print("📊 轮询任务状态...")
|
|
675
675
|
poll_task_status(client, result['job_id'])
|
|
676
676
|
else:
|
|
677
|
-
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
|
|
@@ -52,12 +52,11 @@ cp config.example.yaml config.yaml
|
|
|
52
52
|
base_url: https://szc.test.dp.tech
|
|
53
53
|
auth:
|
|
54
54
|
email: "" # 验证码登录邮箱(留空则必须填 token)
|
|
55
|
-
captcha: "
|
|
55
|
+
captcha: "" # 留空,执行 dma login 时交互输入验证码
|
|
56
56
|
token: "" # 直接配 token(优先于 email 登录)
|
|
57
57
|
uexid: "" # 留空从 JWT 自动解析为 fe_<userId>
|
|
58
58
|
user_id: "" # 留空从 JWT 自动解析
|
|
59
59
|
defaults:
|
|
60
|
-
biz_platform: 2
|
|
61
60
|
name_prefix: "cli-job"
|
|
62
61
|
max_run_time: 15
|
|
63
62
|
max_run_time_unit: d
|
|
@@ -96,6 +95,11 @@ dma login
|
|
|
96
95
|
dma login --email you@example.com
|
|
97
96
|
```
|
|
98
97
|
|
|
98
|
+
`auth.captcha` 留空时,登录会提示 `请输入邮箱验证码:`,输入本次验证码后提交。
|
|
99
|
+
验证码不会保存到配置文件或登录缓存。若配置中已填写 `auth.captcha`,则使用该值;
|
|
100
|
+
已有配置中的旧验证码请清空,以启用交互输入。输入为空或取消输入时,不发送登录请求。
|
|
101
|
+
此命令只校验输入的验证码,不会自动发送验证码邮件。
|
|
102
|
+
|
|
99
103
|
解析 JWT 缓存 `token` / `user_id` / `uexid` 到 `~/.dma_cli/credentials.json`。也可跳过登录,直接在 `config.yaml` 填 `token`。
|
|
100
104
|
|
|
101
105
|
### whoami —— 查看当前状态
|
|
@@ -108,12 +112,19 @@ dma whoami
|
|
|
108
112
|
|
|
109
113
|
### project —— 项目管理
|
|
110
114
|
|
|
115
|
+
先查询当前账号可访问的项目,根据项目名称找到自己的 `projectId`,再设为当前项目:
|
|
116
|
+
|
|
111
117
|
```bash
|
|
112
|
-
dma project list
|
|
113
|
-
dma project use
|
|
114
|
-
dma project current
|
|
118
|
+
dma project list # 列出 projectId | name
|
|
119
|
+
dma project use <你的项目ID> # 替换为上一步查到的 projectId
|
|
120
|
+
dma project current # 确认当前项目 ID 和名称
|
|
115
121
|
```
|
|
116
122
|
|
|
123
|
+
`<你的项目ID>` 是占位符,执行前请替换为列表中你要使用的数字 ID,不要输入尖括号。
|
|
124
|
+
当前项目会保存在本机,后续提交任务时省略 `--project_id` 或设置为 `0` 即使用该项目;
|
|
125
|
+
JSON 配置中的 `"project_id": 0` 含义相同。`0` 表示使用当前项目,不是真实项目 ID,首次提交前需先完成项目选择。
|
|
126
|
+
如需单次提交到其他项目,可传入 `--project_id <你的项目ID>`,不会改变已保存的当前项目。
|
|
127
|
+
|
|
117
128
|
### sku —— 机型查询
|
|
118
129
|
|
|
119
130
|
```bash
|
|
@@ -146,7 +157,7 @@ dma job submit -i job.example.json
|
|
|
146
157
|
dma job submit \
|
|
147
158
|
--machine_type c2_m2_cpu \
|
|
148
159
|
--image_address ubuntu:22.04-py3.10-terminal-20260916 \
|
|
149
|
-
--project_id
|
|
160
|
+
--project_id 0 \
|
|
150
161
|
--command "python calc.py" \
|
|
151
162
|
--job_name my-job
|
|
152
163
|
```
|
|
@@ -192,7 +203,7 @@ dma job submit \
|
|
|
192
203
|
# {"job_name": "job-002", "machine_type": "c4_m8_cpu", ...}
|
|
193
204
|
# ]
|
|
194
205
|
|
|
195
|
-
dma job submit --batch_file batch.json --concurrency 3
|
|
206
|
+
dma job submit --batch_file batch.example.json --concurrency 3
|
|
196
207
|
```
|
|
197
208
|
|
|
198
209
|
### 方式三:--batch_input_dir(目录扫描)
|
|
@@ -200,7 +211,7 @@ dma job submit --batch_file batch.json --concurrency 3
|
|
|
200
211
|
```bash
|
|
201
212
|
# 每个子目录作为一个任务的输入
|
|
202
213
|
dma job submit \
|
|
203
|
-
--batch_input_dir ./
|
|
214
|
+
--batch_input_dir ./input \
|
|
204
215
|
--machine_type c2_m2_cpu \
|
|
205
216
|
--image_address ubuntu:22.04-py3.10-terminal-20260916 \
|
|
206
217
|
--command "python calc.py" \
|
|
@@ -217,9 +228,10 @@ cp config.example.yaml config.yaml
|
|
|
217
228
|
# 2. 查看状态
|
|
218
229
|
dma whoami
|
|
219
230
|
|
|
220
|
-
# 3.
|
|
231
|
+
# 3. 查询并选择自己的项目(将占位符替换为列表中的 projectId,不保留尖括号)
|
|
221
232
|
dma project list
|
|
222
|
-
dma project use
|
|
233
|
+
dma project use <你的项目ID>
|
|
234
|
+
dma project current
|
|
223
235
|
|
|
224
236
|
# 4. 查询机型和镜像
|
|
225
237
|
dma sku list --kind cpu
|
|
@@ -229,5 +241,5 @@ dma image list
|
|
|
229
241
|
dma job submit -i job.example.json
|
|
230
242
|
|
|
231
243
|
# 6. 批量提交
|
|
232
|
-
dma job submit --batch_file batch.json
|
|
244
|
+
dma job submit --batch_file batch.example.json
|
|
233
245
|
```
|
|
@@ -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
|
|
File without changes
|