compshare-cli 0.1.0__py3-none-any.whl
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.
- compshare_cli/__init__.py +3 -0
- compshare_cli/__main__.py +4 -0
- compshare_cli/actions.py +71 -0
- compshare_cli/api.py +127 -0
- compshare_cli/cli.py +219 -0
- compshare_cli/commands/__init__.py +1 -0
- compshare_cli/commands/common.py +57 -0
- compshare_cli/commands/image.py +528 -0
- compshare_cli/commands/instance.py +1493 -0
- compshare_cli/commands/storage.py +276 -0
- compshare_cli/config.py +127 -0
- compshare_cli/errors.py +10 -0
- compshare_cli/i18n.py +506 -0
- compshare_cli/location.py +91 -0
- compshare_cli/output.py +124 -0
- compshare_cli/parsing.py +93 -0
- compshare_cli/runtime.py +34 -0
- compshare_cli/sdk.py +27 -0
- compshare_cli-0.1.0.dist-info/METADATA +283 -0
- compshare_cli-0.1.0.dist-info/RECORD +23 -0
- compshare_cli-0.1.0.dist-info/WHEEL +5 -0
- compshare_cli-0.1.0.dist-info/entry_points.txt +2 -0
- compshare_cli-0.1.0.dist-info/top_level.txt +1 -0
compshare_cli/i18n.py
ADDED
|
@@ -0,0 +1,506 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from typing import Dict
|
|
5
|
+
|
|
6
|
+
import click
|
|
7
|
+
|
|
8
|
+
from compshare_cli.config import ConfigStore
|
|
9
|
+
from compshare_cli.errors import ConfigError, UsageError
|
|
10
|
+
|
|
11
|
+
DEFAULT_LANGUAGE = "zh"
|
|
12
|
+
|
|
13
|
+
ZH_TRANSLATIONS: Dict[str, str] = {
|
|
14
|
+
"Manage CompShare GPU compute from the terminal.": "在终端管理优云智算 GPU 计算资源。",
|
|
15
|
+
"Emit machine-readable JSON.": "输出机器可读的 JSON。",
|
|
16
|
+
"Credential profile.": "凭证配置名称。",
|
|
17
|
+
"Credential profile name.": "凭证配置的名称。",
|
|
18
|
+
"API public key.": "API 公钥。",
|
|
19
|
+
"Make this profile the default.": "将此凭证设为默认配置。",
|
|
20
|
+
"Region for this request; not part of the credential profile.": (
|
|
21
|
+
"本次请求的地域,不属于凭证配置。"
|
|
22
|
+
),
|
|
23
|
+
"Availability zone for this request; not part of the credential profile.": (
|
|
24
|
+
"本次请求的可用区,不属于凭证配置。"
|
|
25
|
+
),
|
|
26
|
+
"Set or show the default help language.": "设置或查看默认帮助语言。",
|
|
27
|
+
"Language: zh or en.": "语言:zh 或 en。",
|
|
28
|
+
"Save a CompShare API credential profile.": "保存优云智算 API 凭证配置。",
|
|
29
|
+
"Print the CLI version.": "显示 CLI 版本。",
|
|
30
|
+
"Manage GPU instances.": "管理 GPU 实例。",
|
|
31
|
+
"Search legal specifications and, with --image, real inventory.": (
|
|
32
|
+
"查询合法规格;指定 --image 时检查真实库存。"
|
|
33
|
+
),
|
|
34
|
+
"GPU type; repeatable.": "GPU 型号,可重复指定。",
|
|
35
|
+
"Search interruptible instances.": "查询抢占式实例。",
|
|
36
|
+
"Image ID. When set, check real inventory for every matched GPU type.": (
|
|
37
|
+
"镜像 ID;指定后检查每个匹配 GPU 型号的真实库存。"
|
|
38
|
+
),
|
|
39
|
+
"Show only in-stock specs.": "只显示有库存的规格。",
|
|
40
|
+
"Availability zone.": "可用区。",
|
|
41
|
+
"Availability zone for this request.": "本次请求的可用区。",
|
|
42
|
+
"CPU platform for stock checks.": "库存检查使用的 CPU 平台。",
|
|
43
|
+
"Billing type for stock checks.": "库存检查使用的计费方式。",
|
|
44
|
+
"Boot disk size for stock checks.": "库存检查使用的系统盘大小。",
|
|
45
|
+
"List supported regions and availability zones.": "列出支持的地域和可用区。",
|
|
46
|
+
"List GPU machine families.": "列出 GPU 机型族。",
|
|
47
|
+
"List instances.": "列出实例。",
|
|
48
|
+
"Instance ID; repeatable.": "实例 ID,可重复指定。",
|
|
49
|
+
"Filter hosts compatible with a disk.": "筛选可挂载指定云盘的实例。",
|
|
50
|
+
"Project ID for this request.": "本次请求使用的项目 ID。",
|
|
51
|
+
"Show full instance details.": "显示实例完整信息。",
|
|
52
|
+
"Instance ID.": "实例 ID。",
|
|
53
|
+
"Create instances interactively, or use explicit options for automation.": (
|
|
54
|
+
"通过交互向导创建实例,也可显式传参用于自动化。"
|
|
55
|
+
),
|
|
56
|
+
"GPU type, for example 4090.": "GPU 型号,例如 4090。",
|
|
57
|
+
"GPU count.": "GPU 数量。",
|
|
58
|
+
"Memory, for example 64GiB.": "内存大小,例如 64GiB。",
|
|
59
|
+
"CompShare image ID.": "优云智算镜像 ID。",
|
|
60
|
+
"Image source: platform, custom, community or shared.": (
|
|
61
|
+
"镜像来源:platform、custom、community 或 shared。"
|
|
62
|
+
),
|
|
63
|
+
"Boot disk size.": "系统盘大小。",
|
|
64
|
+
"Boot disk type.": "系统盘类型。",
|
|
65
|
+
"Data disk as SIZE[:TYPE]; repeatable.": "数据盘,格式为 SIZE[:TYPE],可重复指定。",
|
|
66
|
+
"Billing type.": "计费方式。",
|
|
67
|
+
"Skip confirmation.": "跳过确认。",
|
|
68
|
+
"A or B.": "无卡规格 A 或 B。",
|
|
69
|
+
"Start an instance.": "启动实例。",
|
|
70
|
+
"Stop an instance.": "停止实例。",
|
|
71
|
+
"Reboot an instance.": "重启实例。",
|
|
72
|
+
"Permanently delete an instance.": "永久删除实例。",
|
|
73
|
+
"Rename an instance.": "修改实例名称。",
|
|
74
|
+
"Reset an instance password.": "重置实例密码。",
|
|
75
|
+
"Reinstall an instance from an image.": "使用镜像重装实例。",
|
|
76
|
+
"Change instance CPU, memory, GPU or disk size.": "调整实例的 CPU、内存、GPU 或磁盘大小。",
|
|
77
|
+
"Query a new instance price.": "查询新实例价格。",
|
|
78
|
+
"Query the price of an instance upgrade.": "查询实例升配价格。",
|
|
79
|
+
"Query current instance pricing.": "查询实例当前计费价格。",
|
|
80
|
+
"Query instance refund amounts.": "查询实例退款金额。",
|
|
81
|
+
"Get instance monitoring data.": "获取实例监控数据。",
|
|
82
|
+
"Get instance monitoring data (currently unavailable in production).": (
|
|
83
|
+
"获取实例监控数据(生产环境当前不可用)。"
|
|
84
|
+
),
|
|
85
|
+
"Change an instance billing type.": "变更实例计费方式。",
|
|
86
|
+
"Check network accelerator status.": "检查网络加速状态。",
|
|
87
|
+
"List models in the model repository.": "列出模型仓库中的模型。",
|
|
88
|
+
"Shared storage as SIZE[:TYPE]; repeatable.": "共享存储,格式为 SIZE[:TYPE],可重复指定。",
|
|
89
|
+
"Month, Day, Dynamic or Postpay.": "目标计费方式:Month、Day、Dynamic 或 Postpay。",
|
|
90
|
+
"Print instead of executing SSH.": "只显示 SSH 命令,不执行连接。",
|
|
91
|
+
"Open or print an instance SSH command.": "连接实例 SSH,或仅显示 SSH 命令。",
|
|
92
|
+
"Manage container port mappings.": "管理容器端口映射。",
|
|
93
|
+
"List supported software ports.": "列出平台支持的软件端口。",
|
|
94
|
+
"Replace an instance's container port mappings.": "替换实例的容器端口映射。",
|
|
95
|
+
"Manage scheduled shutdowns.": "管理定时关机。",
|
|
96
|
+
"Schedule an instance shutdown.": "设置实例定时关机。",
|
|
97
|
+
"Cancel an instance scheduled shutdown.": "取消实例定时关机。",
|
|
98
|
+
"Unix timestamp or ISO 8601 time.": "Unix 时间戳或 ISO 8601 时间。",
|
|
99
|
+
"Discover software exposed by instances.": "查询实例提供的软件入口。",
|
|
100
|
+
"List supported instance software.": "列出实例支持的软件。",
|
|
101
|
+
"Get an instance software access URL.": "获取实例软件的访问地址。",
|
|
102
|
+
"Get an instance software access URL (currently unavailable in production).": (
|
|
103
|
+
"获取实例软件的访问地址(生产环境当前不可用)。"
|
|
104
|
+
),
|
|
105
|
+
"Manage instance images.": "管理实例镜像。",
|
|
106
|
+
"List platform, custom, community, shared or published images.": (
|
|
107
|
+
"列出平台、自制、社区、共享或已发布镜像。"
|
|
108
|
+
),
|
|
109
|
+
"Fuzzy name or author search.": "按名称或作者模糊搜索。",
|
|
110
|
+
"Community sort field.": "社区镜像排序字段。",
|
|
111
|
+
"Organization ID for source=user.": "source=user 时使用的组织 ID。",
|
|
112
|
+
"Create a custom image from an instance.": "从实例创建自制镜像。",
|
|
113
|
+
"Show image details.": "显示镜像详情。",
|
|
114
|
+
"Get custom image creation progress.": "查询自制镜像创建进度。",
|
|
115
|
+
"Update image metadata.": "更新镜像元数据。",
|
|
116
|
+
"Permanently delete a custom image.": "永久删除自制镜像。",
|
|
117
|
+
"List accounts an image is shared with.": "列出镜像已共享的账户。",
|
|
118
|
+
"Share an image with accounts.": "将镜像共享给指定账户。",
|
|
119
|
+
"Remove image sharing from accounts.": "取消向指定账户共享镜像。",
|
|
120
|
+
"Publish an image to the community.": "将镜像发布到社区。",
|
|
121
|
+
"Add an image to favorites.": "收藏镜像。",
|
|
122
|
+
"Remove an image from favorites.": "取消收藏镜像。",
|
|
123
|
+
"List available image tags.": "列出可用的镜像标签。",
|
|
124
|
+
"Manage disks and cloud storage.": "管理磁盘和云存储。",
|
|
125
|
+
"Manage instance disks.": "管理实例云盘。",
|
|
126
|
+
"List disks reported by one or all instances.": "列出一个或全部实例的磁盘。",
|
|
127
|
+
"Create a disk and attach it to an instance.": "创建云盘并挂载到实例。",
|
|
128
|
+
"Disk size, for example 100GiB.": "磁盘大小,例如 100GiB。",
|
|
129
|
+
"Device path, for example /dev/vdb.": "设备路径,例如 /dev/vdb。",
|
|
130
|
+
"Attach an existing disk to an instance.": "将已有云盘挂载到实例。",
|
|
131
|
+
"Detach a disk from an instance.": "从实例卸载云盘。",
|
|
132
|
+
"Query a disk expansion price.": "查询云盘扩容价格。",
|
|
133
|
+
"Resize a disk.": "扩容云盘。",
|
|
134
|
+
"Permanently delete a disk.": "永久删除云盘。",
|
|
135
|
+
"Manage US3 attachments.": "管理 US3 挂载。",
|
|
136
|
+
"Attach US3 object storage to an instance.": "将 US3 对象存储挂载到实例。",
|
|
137
|
+
"No results. Try adjusting the filters or checking the selected region.": (
|
|
138
|
+
"没有结果,请调整筛选条件或检查所选地域。"
|
|
139
|
+
),
|
|
140
|
+
"Error": "错误",
|
|
141
|
+
"yes": "是",
|
|
142
|
+
"no": "否",
|
|
143
|
+
"Public key": "公钥",
|
|
144
|
+
"Private key": "私钥",
|
|
145
|
+
"New password": "新密码",
|
|
146
|
+
"Select": "请选择",
|
|
147
|
+
"Aborted": "操作已取消",
|
|
148
|
+
"Attach US3 during container creation.": "创建容器实例时挂载 US3。",
|
|
149
|
+
"Attached instance ID.": "已挂载的实例 ID。",
|
|
150
|
+
"Backup mode: NONE, DATAARK or SNAPSHOT.": "备份模式:NONE、DATAARK 或 SNAPSHOT。",
|
|
151
|
+
"Billing duration for prepaid modes.": "预付费模式的购买时长。",
|
|
152
|
+
"Boot disk type used for stock checks.": "库存检查使用的系统盘类型。",
|
|
153
|
+
"CPU core count.": "CPU 核数。",
|
|
154
|
+
"Community image description.": "社区镜像描述。",
|
|
155
|
+
"Community image name.": "社区镜像名称。",
|
|
156
|
+
"Community version group ID.": "社区镜像版本组 ID。",
|
|
157
|
+
"Community version name.": "社区镜像版本名称。",
|
|
158
|
+
"Complete HTTP port list; repeatable.": "完整 HTTP 端口列表,可重复指定。",
|
|
159
|
+
"Complete TCP port list; repeatable.": "完整 TCP 端口列表,可重复指定。",
|
|
160
|
+
"Coupon ID.": "代金券 ID。",
|
|
161
|
+
"Cover image file encoded as Base64.": "封面图片文件,将编码为 Base64。",
|
|
162
|
+
"Custom image description.": "自制镜像描述。",
|
|
163
|
+
"Custom image name.": "自制镜像名称。",
|
|
164
|
+
"Data disk type.": "数据盘类型。",
|
|
165
|
+
"Delete attached data disks with the instance.": "删除实例时同时删除已挂载的数据盘。",
|
|
166
|
+
"Disk ID to resize.": "要扩容的磁盘 ID。",
|
|
167
|
+
"Disk name.": "磁盘名称。",
|
|
168
|
+
"Disk type.": "磁盘类型。",
|
|
169
|
+
"Existing community version group ID.": "已有社区镜像版本组 ID。",
|
|
170
|
+
"Filter by VPC ID.": "按 VPC ID 筛选。",
|
|
171
|
+
"Filter by automatic startup support.": "按是否支持自动启动筛选。",
|
|
172
|
+
"Filter by availability zone.": "按可用区筛选。",
|
|
173
|
+
"Filter by exact image name.": "按镜像完整名称筛选。",
|
|
174
|
+
"Filter by image ID.": "按镜像 ID 筛选。",
|
|
175
|
+
"Filter by image author.": "按镜像作者筛选。",
|
|
176
|
+
"Filter by image type.": "按镜像类型筛选。",
|
|
177
|
+
"Filter by instance ID.": "按实例 ID 筛选。",
|
|
178
|
+
"Filter by instance tag.": "按实例标签筛选。",
|
|
179
|
+
"Filter by model name.": "按模型名称筛选。",
|
|
180
|
+
"Filter by model tags.": "按模型标签筛选。",
|
|
181
|
+
"Filter by subnet ID.": "按子网 ID 筛选。",
|
|
182
|
+
"Filter by tag; repeatable.": "按标签筛选,可重复指定。",
|
|
183
|
+
"Filter by version group ID.": "按版本组 ID 筛选。",
|
|
184
|
+
"Filter free or paid community images.": "筛选免费或付费社区镜像。",
|
|
185
|
+
"Filter official or community-authored images.": "筛选官方或社区作者镜像。",
|
|
186
|
+
"GPU type.": "GPU 型号。",
|
|
187
|
+
"Hourly image price.": "镜像每小时价格。",
|
|
188
|
+
"Hourly image price; 0 means free.": "镜像每小时价格,0 表示免费。",
|
|
189
|
+
"Image ID for image pricing.": "用于镜像计价的镜像 ID。",
|
|
190
|
+
"Image source.": "镜像来源。",
|
|
191
|
+
"Image source: platform, custom, community, shared, published or user.": (
|
|
192
|
+
"镜像来源:platform、custom、community、shared、published 或 user。"
|
|
193
|
+
),
|
|
194
|
+
"Image tag; repeatable.": "镜像标签,可重复指定。",
|
|
195
|
+
"Instance name.": "实例名称。",
|
|
196
|
+
"Instance remark.": "实例备注。",
|
|
197
|
+
"List no-GPU instances.": "列出无 GPU 实例。",
|
|
198
|
+
"Maximum number of results.": "最多返回的结果数量。",
|
|
199
|
+
"Minimum CPU platform.": "最低 CPU 平台。",
|
|
200
|
+
"New image description.": "新的镜像描述。",
|
|
201
|
+
"New image name.": "新的镜像名称。",
|
|
202
|
+
"No-GPU specification: A or B.": "无卡规格:A 或 B。",
|
|
203
|
+
"Number of instances.": "实例数量。",
|
|
204
|
+
"Number of results to skip.": "跳过的结果数量。",
|
|
205
|
+
"Replacement image ID.": "重装使用的镜像 ID。",
|
|
206
|
+
"Security group ID.": "安全组 ID。",
|
|
207
|
+
"Sort in ascending order.": "按升序排列。",
|
|
208
|
+
"Source instance ID.": "源实例 ID。",
|
|
209
|
+
"Supported GPU type; repeatable.": "支持的 GPU 型号,可重复指定。",
|
|
210
|
+
"Target CPU core count.": "目标 CPU 核数。",
|
|
211
|
+
"Target GPU count.": "目标 GPU 数量。",
|
|
212
|
+
"Target disk size, for example 200GiB.": "目标磁盘大小,例如 200GiB。",
|
|
213
|
+
"Target instance ID.": "目标实例 ID。",
|
|
214
|
+
"Target memory, for example 64GiB.": "目标内存,例如 64GiB。",
|
|
215
|
+
"Target no-GPU specification: A or B.": "目标无卡规格:A 或 B。",
|
|
216
|
+
"Target running instance ID.": "目标运行中实例 ID。",
|
|
217
|
+
"UTF-8 README file.": "UTF-8 编码的 README 文件。",
|
|
218
|
+
"Version description.": "版本描述。",
|
|
219
|
+
"Version name.": "版本名称。",
|
|
220
|
+
"Visibility: 0 private, 1 public.": "可见性:0 私有,1 公开。",
|
|
221
|
+
"Whether the image supports automatic startup.": "镜像是否支持自动启动。",
|
|
222
|
+
"Saved credential profile {name}": "已保存凭证配置 {name}",
|
|
223
|
+
"Invalid memory size: {value}. Example: 64GiB": "无效的内存大小:{value}。示例:64GiB",
|
|
224
|
+
"Memory must be positive and resolve to whole GiB.": "内存必须大于 0,且换算后为整数 GiB。",
|
|
225
|
+
"Invalid disk size: {value}. Example: 100GiB": "无效的磁盘大小:{value}。示例:100GiB",
|
|
226
|
+
"Disk MiB must resolve to whole GiB.": "磁盘 MiB 数必须能换算成整数 GiB。",
|
|
227
|
+
"Disk size must be positive.": "磁盘大小必须大于 0。",
|
|
228
|
+
"Time must be a Unix timestamp, ISO 8601 value, or relative value like 30m or 2h.": (
|
|
229
|
+
"时间必须是 Unix 时间戳、ISO 8601 时间,或 30m、2h 这样的相对时间。"
|
|
230
|
+
),
|
|
231
|
+
"Unable to read file {path}: {error}": "无法读取文件 {path}:{error}",
|
|
232
|
+
"Wait for the operation to reach a stable state.": "等待操作进入稳定状态。",
|
|
233
|
+
"Maximum wait time in seconds.": "最长等待时间(秒)。",
|
|
234
|
+
"Validate and show the request without changing resources.": "仅校验并显示请求,不变更资源。",
|
|
235
|
+
"Operation plan": "操作计划",
|
|
236
|
+
"Create plan": "创建计划",
|
|
237
|
+
"Instance details": "实例详情",
|
|
238
|
+
"Image details": "镜像详情",
|
|
239
|
+
"Storage details": "存储详情",
|
|
240
|
+
"Confirm this operation?": "确认执行该操作?",
|
|
241
|
+
"Dry run completed; no resources were changed.": "预演完成,未变更任何资源。",
|
|
242
|
+
"Waiting for {instance}: {state}": "正在等待 {instance}:{state}",
|
|
243
|
+
"Timed out after {timeout}s while waiting for {instance}.": (
|
|
244
|
+
"等待 {instance} 超过 {timeout} 秒。可以用 `compshare instance show {instance}` 检查状态。"
|
|
245
|
+
),
|
|
246
|
+
"Operation submitted": "操作已提交",
|
|
247
|
+
"Operation completed": "操作已完成",
|
|
248
|
+
"No selectable options": "没有可选项",
|
|
249
|
+
"Automatically selected the only option.": "已自动选择唯一选项。",
|
|
250
|
+
"Please enter a number from 1 to {count}.": "请输入 1 到 {count} 之间的数字。",
|
|
251
|
+
"Availability zone": "可用区",
|
|
252
|
+
"GPU type": "GPU 型号",
|
|
253
|
+
"Image source": "镜像来源",
|
|
254
|
+
"Image": "镜像",
|
|
255
|
+
"Billing type": "计费方式",
|
|
256
|
+
"Available specification": "可用规格",
|
|
257
|
+
"Filter images by name or ID (blank shows all)": "按名称或 ID 筛选镜像(留空显示全部)",
|
|
258
|
+
"Boot disk size": "系统盘大小",
|
|
259
|
+
"Boot disk type": "系统盘类型",
|
|
260
|
+
"Password": "密码",
|
|
261
|
+
"PROFILE": "配置",
|
|
262
|
+
"ACTIVE": "当前",
|
|
263
|
+
"ID": "ID",
|
|
264
|
+
"NAME": "名称",
|
|
265
|
+
"STATE": "状态",
|
|
266
|
+
"GPU": "GPU",
|
|
267
|
+
"COUNT": "数量",
|
|
268
|
+
"CPU": "CPU",
|
|
269
|
+
"MEMORY": "内存",
|
|
270
|
+
"ZONE": "可用区",
|
|
271
|
+
"REGION": "地域",
|
|
272
|
+
"CHARGE": "计费",
|
|
273
|
+
"PRICE/H": "每小时价格",
|
|
274
|
+
"VRAM": "显存",
|
|
275
|
+
"IN STOCK": "有库存",
|
|
276
|
+
"CPU PLATFORM": "CPU 平台",
|
|
277
|
+
"DESCRIPTION": "描述",
|
|
278
|
+
"INSTANCE": "实例",
|
|
279
|
+
"IMAGE": "镜像",
|
|
280
|
+
"SYSTEM DISK": "系统盘",
|
|
281
|
+
"DATA DISKS": "数据盘",
|
|
282
|
+
"REFUND": "退款",
|
|
283
|
+
"CODE": "代码",
|
|
284
|
+
"MESSAGE": "信息",
|
|
285
|
+
"PATH": "路径",
|
|
286
|
+
"TAG": "标签",
|
|
287
|
+
"SIZE": "大小",
|
|
288
|
+
"CREATED": "创建时间",
|
|
289
|
+
"SOFTWARE": "软件",
|
|
290
|
+
"PORT": "端口",
|
|
291
|
+
"TYPE": "类型",
|
|
292
|
+
"AUTHOR": "作者",
|
|
293
|
+
"STATUS": "状态",
|
|
294
|
+
"VERSION": "版本",
|
|
295
|
+
"TAGS": "标签",
|
|
296
|
+
"ACCOUNT ID": "账户 ID",
|
|
297
|
+
"ACCOUNT": "账户",
|
|
298
|
+
"DISK ID": "云盘 ID",
|
|
299
|
+
"BOOT": "系统盘",
|
|
300
|
+
"DEVICE": "设备",
|
|
301
|
+
"Manage credential profiles.": "管理 API 凭证配置。",
|
|
302
|
+
"Create or update a credential profile.": "创建或更新凭证配置。",
|
|
303
|
+
"List credential profiles.": "列出凭证配置。",
|
|
304
|
+
"Set the default credential profile.": "设置默认凭证配置。",
|
|
305
|
+
"Delete a credential profile.": "删除凭证配置。",
|
|
306
|
+
"Show the configuration file path.": "显示配置文件路径。",
|
|
307
|
+
"Filter by instance name.": "按实例名称筛选。",
|
|
308
|
+
"Filter by instance state.": "按实例状态筛选。",
|
|
309
|
+
"Filter by GPU type.": "按 GPU 型号筛选。",
|
|
310
|
+
"Filter by billing type.": "按计费方式筛选。",
|
|
311
|
+
"Filter by region.": "按地域筛选。",
|
|
312
|
+
"Region for this request.": "本次请求的地域。",
|
|
313
|
+
"Unix timestamp, ISO 8601, or relative time.": "Unix 时间戳、ISO 8601 或相对时间。",
|
|
314
|
+
"Override the automatically detected project ID.": "覆盖自动检测的项目 ID。",
|
|
315
|
+
"Using credential profile {name}": "已切换到凭证配置 {name}",
|
|
316
|
+
"Delete credential profile {name}?": "删除凭证配置 {name}?",
|
|
317
|
+
"Deleted credential profile {name}": "已删除凭证配置 {name}",
|
|
318
|
+
"--available requires --image because inventory depends on the image and disks.": (
|
|
319
|
+
"--available 需要同时指定 --image,库存取决于镜像和磁盘组合。"
|
|
320
|
+
),
|
|
321
|
+
"--disk and --disk-size must be used together.": "--disk 和 --disk-size 必须同时使用。",
|
|
322
|
+
"--image-source must be platform, custom, community, or shared.": (
|
|
323
|
+
"--image-source 必须是 platform、custom、community 或 shared。"
|
|
324
|
+
),
|
|
325
|
+
"--source must be platform, custom, community, shared, published, or user.": (
|
|
326
|
+
"--source 必须是 platform、custom、community、shared、published 或 user。"
|
|
327
|
+
),
|
|
328
|
+
"--without-gpu cannot be combined with --cpu, --memory, or --gpu.": (
|
|
329
|
+
"--without-gpu 不能与 --cpu、--memory 或 --gpu 同时使用。"
|
|
330
|
+
),
|
|
331
|
+
"Hint": "建议",
|
|
332
|
+
"The machine type API returned an unnamed GPU.": "机型接口返回了未命名的 GPU。",
|
|
333
|
+
"More than 50 images matched; use a more specific filter.": (
|
|
334
|
+
"匹配到超过 50 个镜像,请输入更具体的筛选词。"
|
|
335
|
+
),
|
|
336
|
+
"Unnamed": "未命名",
|
|
337
|
+
"Instance {instance} was not found.": "未找到实例 {instance}。",
|
|
338
|
+
"Instance {instance} was not found in any supported region.": (
|
|
339
|
+
"在所有支持的地域中都未找到实例 {instance}。"
|
|
340
|
+
),
|
|
341
|
+
"Attached disk {disk} was not found in any supported region; pass --zone if it is detached.": (
|
|
342
|
+
"在所有支持的地域中都未找到已挂载云盘 {disk};如果它已卸载,请指定 --zone。"
|
|
343
|
+
),
|
|
344
|
+
"Image {image} was not found.": "未找到镜像 {image}。",
|
|
345
|
+
"No project was returned by GetProjectList; pass --project-id explicitly.": (
|
|
346
|
+
"GetProjectList 未返回项目,请显式指定 --project-id。"
|
|
347
|
+
),
|
|
348
|
+
(
|
|
349
|
+
"JSON mode cannot start the interactive wizard; pass --gpu, --count, --cpu, "
|
|
350
|
+
"--memory, and --image."
|
|
351
|
+
): ("JSON 模式不会启动交互向导,请指定 --gpu、--count、--cpu、--memory 和 --image。"),
|
|
352
|
+
(
|
|
353
|
+
"No inventory is available for the selected GPU, CPU, memory, image, billing, "
|
|
354
|
+
"and disk combination."
|
|
355
|
+
): ("所选 GPU、CPU、内存、镜像、计费方式和磁盘组合当前无库存。"),
|
|
356
|
+
"Stop instance": "停止实例",
|
|
357
|
+
"Reboot instance": "重启实例",
|
|
358
|
+
"Permanently delete instance": "永久删除实例",
|
|
359
|
+
"Permanently delete instance and attached data disks": "永久删除实例及其数据盘",
|
|
360
|
+
"Reset instance password": "重置实例密码",
|
|
361
|
+
"Reinstall instance; all system disk data will be lost": "重装实例,系统盘数据将全部丢失",
|
|
362
|
+
"Resize instance": "调整实例规格",
|
|
363
|
+
"Change instance billing type": "变更实例计费方式",
|
|
364
|
+
"Renamed {instance}": "已重命名实例 {instance}",
|
|
365
|
+
"Specify a compute target or both --disk and --disk-size.": (
|
|
366
|
+
"请指定计算规格,或同时指定 --disk 和 --disk-size。"
|
|
367
|
+
),
|
|
368
|
+
"Compute resizing and disk resizing must be separate operations.": (
|
|
369
|
+
"计算规格调整和磁盘扩容必须分开执行。"
|
|
370
|
+
),
|
|
371
|
+
"Compute resizing requires --cpu, --memory, and --gpu together.": (
|
|
372
|
+
"调整计算规格时必须同时指定 --cpu、--memory 和 --gpu。"
|
|
373
|
+
),
|
|
374
|
+
"Specify at least one of --cpu, --memory, or --gpu.": (
|
|
375
|
+
"至少指定 --cpu、--memory 或 --gpu 中的一项。"
|
|
376
|
+
),
|
|
377
|
+
"Specify at least one --http or --tcp port.": "至少指定一个 --http 或 --tcp 端口。",
|
|
378
|
+
"Replace port mappings for {instance}?": "替换实例 {instance} 的端口映射?",
|
|
379
|
+
"Updated port mappings": "已更新端口映射",
|
|
380
|
+
"Scheduled shutdown must be at least five minutes from now.": (
|
|
381
|
+
"定时关机时间必须至少晚于当前时间 5 分钟。"
|
|
382
|
+
),
|
|
383
|
+
"Scheduled shutdown": "已设置定时关机",
|
|
384
|
+
"Cancel scheduled shutdown for {instance}?": "取消实例 {instance} 的定时关机?",
|
|
385
|
+
"Cancelled shutdown": "已取消定时关机",
|
|
386
|
+
"Instance {instance} has no SSH login command.": "实例 {instance} 没有可用的 SSH 登录命令。",
|
|
387
|
+
"The API did not return a password. Run `compshare instance password {instance}` to set one.": (
|
|
388
|
+
"API 未返回密码。请运行 `compshare instance password {instance}` 设置密码。"
|
|
389
|
+
),
|
|
390
|
+
"Platform image search supports only one --tag.": "平台镜像查询只支持一个 --tag。",
|
|
391
|
+
"Image source {source} does not support: {options}": "镜像来源 {source} 不支持:{options}",
|
|
392
|
+
"Image source {source} cannot be queried by ID.": "镜像来源 {source} 不支持按 ID 查询。",
|
|
393
|
+
"Create custom image {name} from instance {instance}?": (
|
|
394
|
+
"从实例 {instance} 创建自制镜像 {name}?"
|
|
395
|
+
),
|
|
396
|
+
"Creating image {name}": "正在创建镜像 {name}",
|
|
397
|
+
"Specify at least one field to update.": "至少指定一个要更新的字段。",
|
|
398
|
+
"Updated image {image}": "已更新镜像 {image}",
|
|
399
|
+
"Permanently delete custom image {image}?": "永久删除自制镜像 {image}?",
|
|
400
|
+
"Deleted image {image}": "已删除镜像 {image}",
|
|
401
|
+
"Shared image {image}": "已共享镜像 {image}",
|
|
402
|
+
"Unshared image {image}": "已取消共享镜像 {image}",
|
|
403
|
+
"Remove {count} share(s) from image {image}?": "从镜像 {image} 移除 {count} 个共享账户?",
|
|
404
|
+
"Publish image {image} as community version {version}?": (
|
|
405
|
+
"将镜像 {image} 发布为社区版本 {version}?"
|
|
406
|
+
),
|
|
407
|
+
"Published image {image}": "已发布镜像 {image}",
|
|
408
|
+
"Favorited image {image}": "已收藏镜像 {image}",
|
|
409
|
+
"Unfavorited image {image}": "已取消收藏镜像 {image}",
|
|
410
|
+
"Create {size} disk {name} and attach it to {instance}?": (
|
|
411
|
+
"创建 {size} 云盘 {name} 并挂载到 {instance}?"
|
|
412
|
+
),
|
|
413
|
+
"Created and attached disk {name}": "已创建并挂载云盘 {name}",
|
|
414
|
+
"Attached {disk} to {instance}": "已将云盘 {disk} 挂载到 {instance}",
|
|
415
|
+
"Detach disk {disk} from {instance}? Ensure it is unmounted first.": (
|
|
416
|
+
"从 {instance} 卸载云盘 {disk}?请先在操作系统内卸载文件系统。"
|
|
417
|
+
),
|
|
418
|
+
"Detached disk {disk}": "已卸载云盘 {disk}",
|
|
419
|
+
"Resize disk {disk} to {size}? Disks cannot be shrunk.": (
|
|
420
|
+
"将云盘 {disk} 扩容到 {size}?云盘不能缩容。"
|
|
421
|
+
),
|
|
422
|
+
"Resized disk {disk}": "已扩容云盘 {disk}",
|
|
423
|
+
"Permanently delete disk {disk} and all its data?": "永久删除云盘 {disk} 及其全部数据?",
|
|
424
|
+
"Deleted disk {disk}": "已删除云盘 {disk}",
|
|
425
|
+
"Attached US3 to {instance}": "已将 US3 挂载到 {instance}",
|
|
426
|
+
"Resource is still detaching; retrying ({attempt}/{total})...": (
|
|
427
|
+
"资源仍在卸载,正在重试({attempt}/{total})……"
|
|
428
|
+
),
|
|
429
|
+
"The disk is still detaching. Wait a moment and retry.": "云盘仍在卸载,请稍后重试。",
|
|
430
|
+
"Confirm that US3 is enabled for the selected region and account.": (
|
|
431
|
+
"请确认所选地域和账户已开通 US3。"
|
|
432
|
+
),
|
|
433
|
+
"This production endpoint currently rejects instance IDs; use the console for monitoring.": (
|
|
434
|
+
"生产环境的该接口当前会拒绝实例 ID,请暂时在控制台查看监控。"
|
|
435
|
+
),
|
|
436
|
+
"This production endpoint is currently incompatible; use the console for monitoring.": (
|
|
437
|
+
"生产环境的该接口当前不兼容,请暂时在控制台查看监控。"
|
|
438
|
+
),
|
|
439
|
+
"This production endpoint currently rejects its action; use instance show or the console.": (
|
|
440
|
+
"生产环境的该接口当前拒绝请求,请使用 instance show 或控制台。"
|
|
441
|
+
),
|
|
442
|
+
"PRICE": "价格",
|
|
443
|
+
"ACTION": "操作",
|
|
444
|
+
"REQUEST": "请求",
|
|
445
|
+
"SSH": "SSH",
|
|
446
|
+
"Waiting for image {image}: {state}": "正在等待镜像 {image}:{state}",
|
|
447
|
+
"Image creation failed: {status}": "镜像创建失败:{status}",
|
|
448
|
+
"Timed out after {timeout}s while waiting for image {image}.": (
|
|
449
|
+
"等待镜像 {image} 超过 {timeout} 秒。"
|
|
450
|
+
),
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
def normalize_language(value: str) -> str:
|
|
455
|
+
normalized = value.strip().lower().replace("_", "-")
|
|
456
|
+
if normalized in {"zh", "zh-cn", "cn"}:
|
|
457
|
+
return "zh"
|
|
458
|
+
if normalized in {"en", "en-us", "en-gb"}:
|
|
459
|
+
return "en"
|
|
460
|
+
raise UsageError("Language must be zh or en.")
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
def configured_language() -> str:
|
|
464
|
+
value = os.environ.get("COMPSHARE_LANG")
|
|
465
|
+
if value is None:
|
|
466
|
+
try:
|
|
467
|
+
value = ConfigStore().load_language()
|
|
468
|
+
except ConfigError:
|
|
469
|
+
value = None
|
|
470
|
+
return normalize_language(value or DEFAULT_LANGUAGE)
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
def tr(message: str, **values: object) -> str:
|
|
474
|
+
"""Translate a user-facing string and interpolate named values."""
|
|
475
|
+
template = ZH_TRANSLATIONS.get(message, message) if configured_language() == "zh" else message
|
|
476
|
+
return template.format(**values)
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
def localize_command(command: click.Command, language: str) -> click.Command:
|
|
480
|
+
if language == "en":
|
|
481
|
+
return command
|
|
482
|
+
|
|
483
|
+
def translate(value: str) -> str:
|
|
484
|
+
return ZH_TRANSLATIONS.get(value, value)
|
|
485
|
+
|
|
486
|
+
if command.help:
|
|
487
|
+
command.help = translate(command.help)
|
|
488
|
+
if command.short_help:
|
|
489
|
+
command.short_help = translate(command.short_help)
|
|
490
|
+
for parameter in command.params:
|
|
491
|
+
help_text = getattr(parameter, "help", None)
|
|
492
|
+
if help_text:
|
|
493
|
+
parameter.help = translate(help_text)
|
|
494
|
+
original_help_option = command.get_help_option
|
|
495
|
+
|
|
496
|
+
def localized_help_option(context: click.Context) -> click.Option:
|
|
497
|
+
option = original_help_option(context)
|
|
498
|
+
if option is not None:
|
|
499
|
+
option.help = "显示帮助并退出。"
|
|
500
|
+
return option
|
|
501
|
+
|
|
502
|
+
command.get_help_option = localized_help_option # type: ignore[method-assign]
|
|
503
|
+
if isinstance(command, click.Group):
|
|
504
|
+
for child in command.commands.values():
|
|
505
|
+
localize_command(child, language)
|
|
506
|
+
return command
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Dict, Iterable, Optional, Tuple
|
|
4
|
+
|
|
5
|
+
from compshare_cli.api import call
|
|
6
|
+
from compshare_cli.errors import UsageError
|
|
7
|
+
from compshare_cli.i18n import tr
|
|
8
|
+
from compshare_cli.runtime import Runtime
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def region_from_zone(zone: str) -> str:
|
|
12
|
+
"""Derive CompShare's region identifier from a zone identifier."""
|
|
13
|
+
return zone.rsplit("-", 1)[0] if "-" in zone else zone
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def supported_locations(state: Runtime) -> list[Dict[str, Any]]:
|
|
17
|
+
response = call(state, "DescribeCompShareSupportZone", {"Region": state.region})
|
|
18
|
+
return list(response.get("ZoneInfo", []))
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def supported_regions(state: Runtime) -> list[str]:
|
|
22
|
+
regions = [str(item["Region"]) for item in supported_locations(state) if item.get("Region")]
|
|
23
|
+
return list(dict.fromkeys([state.region, *regions]))
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def candidate_regions(state: Runtime, preferred: Optional[str] = None) -> Iterable[str]:
|
|
27
|
+
if preferred:
|
|
28
|
+
yield preferred
|
|
29
|
+
return
|
|
30
|
+
yield state.region
|
|
31
|
+
for region in supported_regions(state):
|
|
32
|
+
if region != state.region:
|
|
33
|
+
yield region
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def locate_instance(
|
|
37
|
+
state: Runtime,
|
|
38
|
+
instance: str,
|
|
39
|
+
*,
|
|
40
|
+
preferred_region: Optional[str] = None,
|
|
41
|
+
) -> Tuple[str, str, Dict[str, Any]]:
|
|
42
|
+
"""Find an instance across supported regions and return region, zone, and details."""
|
|
43
|
+
for region in candidate_regions(state, preferred_region):
|
|
44
|
+
response = call(
|
|
45
|
+
state,
|
|
46
|
+
"DescribeCompShareInstance",
|
|
47
|
+
{"Region": region, "UHostIds": [instance], "Limit": 1, "Offset": 0},
|
|
48
|
+
)
|
|
49
|
+
hosts = response.get("UHostSet", [])
|
|
50
|
+
if hosts:
|
|
51
|
+
host = dict(hosts[0])
|
|
52
|
+
zone = str(host.get("Zone") or state.zone)
|
|
53
|
+
return region, zone, host
|
|
54
|
+
raise UsageError(
|
|
55
|
+
tr(
|
|
56
|
+
"Instance {instance} was not found in any supported region.",
|
|
57
|
+
instance=instance,
|
|
58
|
+
)
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def locate_disk(
|
|
63
|
+
state: Runtime,
|
|
64
|
+
disk_id: str,
|
|
65
|
+
*,
|
|
66
|
+
preferred_region: Optional[str] = None,
|
|
67
|
+
) -> Tuple[str, str, Optional[Dict[str, Any]], Dict[str, Any]]:
|
|
68
|
+
"""Find an attached disk across supported regions."""
|
|
69
|
+
for region in candidate_regions(state, preferred_region):
|
|
70
|
+
offset = 0
|
|
71
|
+
while True:
|
|
72
|
+
response = call(
|
|
73
|
+
state,
|
|
74
|
+
"DescribeCompShareInstance",
|
|
75
|
+
{"Region": region, "Limit": 100, "Offset": offset},
|
|
76
|
+
)
|
|
77
|
+
hosts = response.get("UHostSet", [])
|
|
78
|
+
for host in hosts:
|
|
79
|
+
for disk in host.get("DiskSet", []):
|
|
80
|
+
if disk.get("UDiskId") == disk_id or disk.get("DiskId") == disk_id:
|
|
81
|
+
return region, str(host.get("Zone") or state.zone), host, disk
|
|
82
|
+
if len(hosts) < 100:
|
|
83
|
+
break
|
|
84
|
+
offset += 100
|
|
85
|
+
raise UsageError(
|
|
86
|
+
tr(
|
|
87
|
+
"Attached disk {disk} was not found in any supported region; "
|
|
88
|
+
"pass --zone if it is detached.",
|
|
89
|
+
disk=disk_id,
|
|
90
|
+
)
|
|
91
|
+
)
|