gogpu 0.1.0__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.
- gogpu-0.1.0/.gitignore +16 -0
- gogpu-0.1.0/LICENSE +21 -0
- gogpu-0.1.0/PKG-INFO +93 -0
- gogpu-0.1.0/README.md +66 -0
- gogpu-0.1.0/pyproject.toml +44 -0
- gogpu-0.1.0/src/gogpu/__init__.py +14 -0
- gogpu-0.1.0/src/gogpu/_version.py +1 -0
- gogpu-0.1.0/src/gogpu/client.py +130 -0
- gogpu-0.1.0/src/gogpu/exceptions.py +37 -0
- gogpu-0.1.0/src/gogpu/resources/__init__.py +1 -0
- gogpu-0.1.0/src/gogpu/resources/account.py +13 -0
- gogpu-0.1.0/src/gogpu/resources/base.py +14 -0
- gogpu-0.1.0/src/gogpu/resources/instances.py +561 -0
gogpu-0.1.0/.gitignore
ADDED
gogpu-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 GoGPU
|
|
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.
|
gogpu-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: gogpu
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python SDK for GoGPU / 捷智算 cloud GPU instances (create, start, stop, reboot, delete).
|
|
5
|
+
Project-URL: Homepage, https://github.com/gogpu/gogpu-sdk
|
|
6
|
+
Project-URL: Documentation, https://github.com/gogpu/gogpu-sdk
|
|
7
|
+
Author: GoGPU
|
|
8
|
+
License: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: cloud,cvm,gogpu,gpu,sdk
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
21
|
+
Requires-Python: >=3.9
|
|
22
|
+
Requires-Dist: httpx>=0.27
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
25
|
+
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# gogpu
|
|
29
|
+
|
|
30
|
+
云主机(CVM)Python SDK:选购、下单、开关机、运维。
|
|
31
|
+
|
|
32
|
+
## Install
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pip install gogpu
|
|
36
|
+
# 开发中:
|
|
37
|
+
# pip install -e .
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Quick start
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
from gogpu import Client
|
|
44
|
+
|
|
45
|
+
client = Client(
|
|
46
|
+
base_url="http://192.168.0.17:3001/api",
|
|
47
|
+
api_key="ak-xxxxxxxx",
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
# 1) 可购买资源(charging 必填语义,默认 hour)
|
|
51
|
+
offers = client.instances.list_offers(page=1, page_size=20, charging="hour")
|
|
52
|
+
good_id = offers["list"][0]["id"]
|
|
53
|
+
|
|
54
|
+
# 2) 下单 + 余额支付
|
|
55
|
+
result = client.instances.create_and_pay(
|
|
56
|
+
good_id=good_id,
|
|
57
|
+
charging="hour",
|
|
58
|
+
image_id=28, # 来自 list_os_images / 控制台
|
|
59
|
+
image_type=3, # 3=系统镜像
|
|
60
|
+
pay_type=1,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
# 3) 已购实例生命周期
|
|
64
|
+
vms = client.instances.list(page=1, page_size=10)
|
|
65
|
+
vm_id = vms["list"][0]["id"]
|
|
66
|
+
client.instances.start(vm_id)
|
|
67
|
+
client.instances.stop(vm_id)
|
|
68
|
+
client.instances.reboot(vm_id)
|
|
69
|
+
client.instances.get_ssh_password(vm_id)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## 云主机方法一览
|
|
73
|
+
|
|
74
|
+
| 类别 | 方法 |
|
|
75
|
+
|---|---|
|
|
76
|
+
| 选购 | `list_offers` `get_offer` `list_gpu_types` `list_regions` `list_os_images` `list_public_images` `list_usable_images` |
|
|
77
|
+
| 订单 | `create` `pay` `create_and_pay` `renew` |
|
|
78
|
+
| 实例 | `list` `get` `statistics` `list_instance_gpu_types` `list_cluster` |
|
|
79
|
+
| 电源 | `start` `stop` `reboot` `delete` `reset_system` `convert_cardless` `stop_cardless` `convert_charging` |
|
|
80
|
+
| 配置 | `edit` `change_password` `change_renew` `change_reserve_disk` `expand` `get_expand_amount` `get_refund_amount` `refund` |
|
|
81
|
+
| 凭据 | `get_ssh_password` `get_vnc_password` `get_rdp_password` `get_jupyter_password` `get_vscode_password` `generate_key` `download_key` |
|
|
82
|
+
| 运维 | `list_ports` `list_default_ports` `update_ports` `get_monitor` `take_screenshot` `create_image` `get_software_config` |
|
|
83
|
+
|
|
84
|
+
常量见 `gogpu.resources.instances`(如 `STATUS_RUNNING`、`ORDER_TYPE_CVM`)。
|
|
85
|
+
|
|
86
|
+
## Auth
|
|
87
|
+
|
|
88
|
+
- `api_key` → 请求头 `x-api-key`
|
|
89
|
+
- `token` → 请求头 `x-token`(JWT)
|
|
90
|
+
|
|
91
|
+
## Response
|
|
92
|
+
|
|
93
|
+
接口统一 `{code, data, msg}`。`code==0` 时方法返回 `data`,否则抛 `GoGPUAPIError`。
|
gogpu-0.1.0/README.md
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# gogpu
|
|
2
|
+
|
|
3
|
+
云主机(CVM)Python SDK:选购、下单、开关机、运维。
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install gogpu
|
|
9
|
+
# 开发中:
|
|
10
|
+
# pip install -e .
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick start
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
from gogpu import Client
|
|
17
|
+
|
|
18
|
+
client = Client(
|
|
19
|
+
base_url="http://192.168.0.17:3001/api",
|
|
20
|
+
api_key="ak-xxxxxxxx",
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
# 1) 可购买资源(charging 必填语义,默认 hour)
|
|
24
|
+
offers = client.instances.list_offers(page=1, page_size=20, charging="hour")
|
|
25
|
+
good_id = offers["list"][0]["id"]
|
|
26
|
+
|
|
27
|
+
# 2) 下单 + 余额支付
|
|
28
|
+
result = client.instances.create_and_pay(
|
|
29
|
+
good_id=good_id,
|
|
30
|
+
charging="hour",
|
|
31
|
+
image_id=28, # 来自 list_os_images / 控制台
|
|
32
|
+
image_type=3, # 3=系统镜像
|
|
33
|
+
pay_type=1,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
# 3) 已购实例生命周期
|
|
37
|
+
vms = client.instances.list(page=1, page_size=10)
|
|
38
|
+
vm_id = vms["list"][0]["id"]
|
|
39
|
+
client.instances.start(vm_id)
|
|
40
|
+
client.instances.stop(vm_id)
|
|
41
|
+
client.instances.reboot(vm_id)
|
|
42
|
+
client.instances.get_ssh_password(vm_id)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## 云主机方法一览
|
|
46
|
+
|
|
47
|
+
| 类别 | 方法 |
|
|
48
|
+
|---|---|
|
|
49
|
+
| 选购 | `list_offers` `get_offer` `list_gpu_types` `list_regions` `list_os_images` `list_public_images` `list_usable_images` |
|
|
50
|
+
| 订单 | `create` `pay` `create_and_pay` `renew` |
|
|
51
|
+
| 实例 | `list` `get` `statistics` `list_instance_gpu_types` `list_cluster` |
|
|
52
|
+
| 电源 | `start` `stop` `reboot` `delete` `reset_system` `convert_cardless` `stop_cardless` `convert_charging` |
|
|
53
|
+
| 配置 | `edit` `change_password` `change_renew` `change_reserve_disk` `expand` `get_expand_amount` `get_refund_amount` `refund` |
|
|
54
|
+
| 凭据 | `get_ssh_password` `get_vnc_password` `get_rdp_password` `get_jupyter_password` `get_vscode_password` `generate_key` `download_key` |
|
|
55
|
+
| 运维 | `list_ports` `list_default_ports` `update_ports` `get_monitor` `take_screenshot` `create_image` `get_software_config` |
|
|
56
|
+
|
|
57
|
+
常量见 `gogpu.resources.instances`(如 `STATUS_RUNNING`、`ORDER_TYPE_CVM`)。
|
|
58
|
+
|
|
59
|
+
## Auth
|
|
60
|
+
|
|
61
|
+
- `api_key` → 请求头 `x-api-key`
|
|
62
|
+
- `token` → 请求头 `x-token`(JWT)
|
|
63
|
+
|
|
64
|
+
## Response
|
|
65
|
+
|
|
66
|
+
接口统一 `{code, data, msg}`。`code==0` 时方法返回 `data`,否则抛 `GoGPUAPIError`。
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.25"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "gogpu"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Python SDK for GoGPU / 捷智算 cloud GPU instances (create, start, stop, reboot, delete)."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [{ name = "GoGPU" }]
|
|
13
|
+
keywords = ["gpu", "cloud", "cvm", "sdk", "gogpu"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 3 - Alpha",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Programming Language :: Python :: 3.9",
|
|
20
|
+
"Programming Language :: Python :: 3.10",
|
|
21
|
+
"Programming Language :: Python :: 3.11",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
"Programming Language :: Python :: 3.13",
|
|
24
|
+
"Topic :: Software Development :: Libraries",
|
|
25
|
+
]
|
|
26
|
+
dependencies = [
|
|
27
|
+
"httpx>=0.27",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[project.optional-dependencies]
|
|
31
|
+
dev = ["pytest>=8.0", "ruff>=0.6"]
|
|
32
|
+
|
|
33
|
+
[project.urls]
|
|
34
|
+
Homepage = "https://github.com/gogpu/gogpu-sdk"
|
|
35
|
+
Documentation = "https://github.com/gogpu/gogpu-sdk"
|
|
36
|
+
|
|
37
|
+
[tool.hatch.build.targets.wheel]
|
|
38
|
+
packages = ["src/gogpu"]
|
|
39
|
+
|
|
40
|
+
[tool.hatch.build.targets.wheel.sources]
|
|
41
|
+
"src/gogpu" = "gogpu"
|
|
42
|
+
|
|
43
|
+
[tool.hatch.build.targets.sdist]
|
|
44
|
+
include = ["src/gogpu", "README.md", "LICENSE", "pyproject.toml"]
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""gogpu — Python SDK for GoGPU cloud GPU instances."""
|
|
2
|
+
|
|
3
|
+
from gogpu._version import __version__
|
|
4
|
+
from gogpu.client import Client
|
|
5
|
+
from gogpu.exceptions import GoGPUAPIError, GoGPUError
|
|
6
|
+
from gogpu.resources import instances as cvm
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"Client",
|
|
10
|
+
"GoGPUAPIError",
|
|
11
|
+
"GoGPUError",
|
|
12
|
+
"cvm",
|
|
13
|
+
"__version__",
|
|
14
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Mapping, MutableMapping, Optional, Union
|
|
4
|
+
|
|
5
|
+
import httpx
|
|
6
|
+
|
|
7
|
+
from gogpu.exceptions import GoGPUAPIError
|
|
8
|
+
from gogpu.resources.account import AccountResource
|
|
9
|
+
from gogpu.resources.instances import InstancesResource
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Client:
|
|
13
|
+
"""GoGPU API client.
|
|
14
|
+
|
|
15
|
+
Parameters
|
|
16
|
+
----------
|
|
17
|
+
base_url:
|
|
18
|
+
API root, e.g. ``http://host:3001/api`` or ``http://host:9021``.
|
|
19
|
+
api_key:
|
|
20
|
+
Account API key, sent as ``x-api-key``.
|
|
21
|
+
token:
|
|
22
|
+
Login JWT, sent as ``x-token``.
|
|
23
|
+
timeout:
|
|
24
|
+
Request timeout in seconds.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(
|
|
28
|
+
self,
|
|
29
|
+
base_url: str,
|
|
30
|
+
*,
|
|
31
|
+
api_key: Optional[str] = None,
|
|
32
|
+
token: Optional[str] = None,
|
|
33
|
+
timeout: float = 60.0,
|
|
34
|
+
transport: Optional[httpx.BaseTransport] = None,
|
|
35
|
+
) -> None:
|
|
36
|
+
if not api_key and not token:
|
|
37
|
+
raise ValueError("Provide at least one of api_key or token")
|
|
38
|
+
|
|
39
|
+
self.base_url = base_url.rstrip("/")
|
|
40
|
+
self.api_key = api_key
|
|
41
|
+
self.token = token
|
|
42
|
+
self.timeout = timeout
|
|
43
|
+
|
|
44
|
+
headers: MutableMapping[str, str] = {
|
|
45
|
+
"Accept": "application/json",
|
|
46
|
+
"Content-Type": "application/json",
|
|
47
|
+
}
|
|
48
|
+
if api_key:
|
|
49
|
+
headers["x-api-key"] = api_key
|
|
50
|
+
if token:
|
|
51
|
+
headers["x-token"] = token
|
|
52
|
+
|
|
53
|
+
self._http = httpx.Client(
|
|
54
|
+
base_url=self.base_url,
|
|
55
|
+
headers=headers,
|
|
56
|
+
timeout=timeout,
|
|
57
|
+
transport=transport,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
self.instances = InstancesResource(self)
|
|
61
|
+
self.account = AccountResource(self)
|
|
62
|
+
|
|
63
|
+
def close(self) -> None:
|
|
64
|
+
self._http.close()
|
|
65
|
+
|
|
66
|
+
def __enter__(self) -> "Client":
|
|
67
|
+
return self
|
|
68
|
+
|
|
69
|
+
def __exit__(self, *args: object) -> None:
|
|
70
|
+
self.close()
|
|
71
|
+
|
|
72
|
+
def request(
|
|
73
|
+
self,
|
|
74
|
+
path: str,
|
|
75
|
+
body: Optional[Mapping[str, Any]] = None,
|
|
76
|
+
*,
|
|
77
|
+
raw: bool = False,
|
|
78
|
+
) -> Any:
|
|
79
|
+
"""POST JSON to ``path`` and unwrap ``{code, data, msg}``.
|
|
80
|
+
|
|
81
|
+
Returns ``data`` when ``code == 0``. Set ``raw=True`` to get the full
|
|
82
|
+
response dict instead.
|
|
83
|
+
"""
|
|
84
|
+
url_path = path if path.startswith("/") else f"/{path}"
|
|
85
|
+
try:
|
|
86
|
+
resp = self._http.post(url_path, json=dict(body or {}))
|
|
87
|
+
except httpx.HTTPError as exc:
|
|
88
|
+
raise GoGPUAPIError(f"HTTP request failed: {exc}", path=url_path) from exc
|
|
89
|
+
|
|
90
|
+
try:
|
|
91
|
+
payload: Union[dict[str, Any], Any] = resp.json()
|
|
92
|
+
except ValueError as exc:
|
|
93
|
+
raise GoGPUAPIError(
|
|
94
|
+
f"Invalid JSON response: {resp.text[:200]}",
|
|
95
|
+
path=url_path,
|
|
96
|
+
status_code=resp.status_code,
|
|
97
|
+
) from exc
|
|
98
|
+
|
|
99
|
+
if resp.status_code >= 400:
|
|
100
|
+
msg = (
|
|
101
|
+
payload.get("msg")
|
|
102
|
+
if isinstance(payload, dict)
|
|
103
|
+
else None
|
|
104
|
+
) or resp.reason_phrase
|
|
105
|
+
raise GoGPUAPIError(
|
|
106
|
+
str(msg),
|
|
107
|
+
code=payload.get("code") if isinstance(payload, dict) else None,
|
|
108
|
+
data=payload.get("data") if isinstance(payload, dict) else payload,
|
|
109
|
+
path=url_path,
|
|
110
|
+
status_code=resp.status_code,
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
if not isinstance(payload, dict) or "code" not in payload:
|
|
114
|
+
if raw:
|
|
115
|
+
return payload
|
|
116
|
+
return payload
|
|
117
|
+
|
|
118
|
+
code = payload.get("code")
|
|
119
|
+
if code != 0:
|
|
120
|
+
raise GoGPUAPIError(
|
|
121
|
+
str(payload.get("msg") or "API error"),
|
|
122
|
+
code=code,
|
|
123
|
+
data=payload.get("data"),
|
|
124
|
+
path=url_path,
|
|
125
|
+
status_code=resp.status_code,
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
if raw:
|
|
129
|
+
return payload
|
|
130
|
+
return payload.get("data")
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Optional
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class GoGPUError(Exception):
|
|
7
|
+
"""Base error for the gogpu SDK."""
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class GoGPUAPIError(GoGPUError):
|
|
11
|
+
"""Raised when the API returns a non-success business code or HTTP error."""
|
|
12
|
+
|
|
13
|
+
def __init__(
|
|
14
|
+
self,
|
|
15
|
+
message: str,
|
|
16
|
+
*,
|
|
17
|
+
code: Optional[int] = None,
|
|
18
|
+
data: Any = None,
|
|
19
|
+
path: Optional[str] = None,
|
|
20
|
+
status_code: Optional[int] = None,
|
|
21
|
+
) -> None:
|
|
22
|
+
super().__init__(message)
|
|
23
|
+
self.message = message
|
|
24
|
+
self.code = code
|
|
25
|
+
self.data = data
|
|
26
|
+
self.path = path
|
|
27
|
+
self.status_code = status_code
|
|
28
|
+
|
|
29
|
+
def __str__(self) -> str:
|
|
30
|
+
parts = [self.message]
|
|
31
|
+
if self.code is not None:
|
|
32
|
+
parts.append(f"code={self.code}")
|
|
33
|
+
if self.path:
|
|
34
|
+
parts.append(f"path={self.path}")
|
|
35
|
+
if self.status_code is not None:
|
|
36
|
+
parts.append(f"http={self.status_code}")
|
|
37
|
+
return " | ".join(parts)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Resource modules for the gogpu SDK."""
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Optional
|
|
4
|
+
|
|
5
|
+
from gogpu.resources.base import BaseResource
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class AccountResource(BaseResource):
|
|
9
|
+
"""User account helpers."""
|
|
10
|
+
|
|
11
|
+
def get_self_info(self, *, id: int = 0) -> Any:
|
|
12
|
+
"""GET current user profile via ``/user/account/GetSelfInfo``."""
|
|
13
|
+
return self._post("/user/account/GetSelfInfo", {"id": id})
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, Any, Mapping, Optional
|
|
4
|
+
|
|
5
|
+
if TYPE_CHECKING:
|
|
6
|
+
from gogpu.client import Client
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class BaseResource:
|
|
10
|
+
def __init__(self, client: "Client") -> None:
|
|
11
|
+
self._client = client
|
|
12
|
+
|
|
13
|
+
def _post(self, path: str, body: Optional[Mapping[str, Any]] = None, **kwargs: Any) -> Any:
|
|
14
|
+
return self._client.request(path, body, **kwargs)
|
|
@@ -0,0 +1,561 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Mapping, Optional, Sequence
|
|
4
|
+
|
|
5
|
+
from gogpu.resources.base import BaseResource
|
|
6
|
+
|
|
7
|
+
# order_type: 1 容器 / 2 云主机 / 5 裸金属 / 7 商品 / 8 Token套餐
|
|
8
|
+
ORDER_TYPE_CVM = 2
|
|
9
|
+
|
|
10
|
+
# CVM status: 1创建中 2开机中 3运行中 4异常 5已关机 6重置中 7已过期 9已释放
|
|
11
|
+
STATUS_CREATING = 1
|
|
12
|
+
STATUS_STARTING = 2
|
|
13
|
+
STATUS_RUNNING = 3
|
|
14
|
+
STATUS_ERROR = 4
|
|
15
|
+
STATUS_STOPPED = 5
|
|
16
|
+
STATUS_RESETTING = 6
|
|
17
|
+
STATUS_EXPIRED = 7
|
|
18
|
+
STATUS_RELEASED = 9
|
|
19
|
+
|
|
20
|
+
# image_type: 1 基础 / 2 模型 / 3 系统 / 7 拷贝云主机 / 9 用户镜像
|
|
21
|
+
IMAGE_TYPE_BASE = 1
|
|
22
|
+
IMAGE_TYPE_MODEL = 2
|
|
23
|
+
IMAGE_TYPE_OS = 3
|
|
24
|
+
IMAGE_TYPE_CVM_COPY = 7
|
|
25
|
+
IMAGE_TYPE_USER = 9
|
|
26
|
+
|
|
27
|
+
# renew mode: 1 自动 / 2 手动
|
|
28
|
+
RENEW_AUTO = 1
|
|
29
|
+
RENEW_MANUAL = 2
|
|
30
|
+
|
|
31
|
+
# reserve disk: 1 保留 / 2 不保留
|
|
32
|
+
RESERVE_DISK_YES = 1
|
|
33
|
+
RESERVE_DISK_NO = 2
|
|
34
|
+
|
|
35
|
+
# monitor mon_type: 1 cpu / 2 内存 / 3 网络 / 4 硬盘
|
|
36
|
+
MON_CPU = 1
|
|
37
|
+
MON_MEMORY = 2
|
|
38
|
+
MON_NETWORK = 3
|
|
39
|
+
MON_DISK = 4
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class InstancesResource(BaseResource):
|
|
43
|
+
"""云主机(CVM)完整能力:选购 → 下单 → 生命周期 → 运维。"""
|
|
44
|
+
|
|
45
|
+
def _by_id(self, path: str, instance_id: int, **extra: Any) -> Any:
|
|
46
|
+
return self._post(path, {"id": instance_id, **extra})
|
|
47
|
+
|
|
48
|
+
# ----- 选购:可购买资源 / 镜像 / 区域 -----
|
|
49
|
+
|
|
50
|
+
def list_offers(
|
|
51
|
+
self,
|
|
52
|
+
*,
|
|
53
|
+
page: int = 1,
|
|
54
|
+
page_size: int = 10,
|
|
55
|
+
charging: str = "hour",
|
|
56
|
+
region_id: Optional[int] = None,
|
|
57
|
+
gpu_id: Optional[int] = None,
|
|
58
|
+
gpu_number: Optional[int] = None,
|
|
59
|
+
keyword: Optional[str] = None,
|
|
60
|
+
server_id: Optional[int] = None,
|
|
61
|
+
web: bool = False,
|
|
62
|
+
**extra: Any,
|
|
63
|
+
) -> Any:
|
|
64
|
+
"""获取可购买的云主机资源列表。
|
|
65
|
+
|
|
66
|
+
返回 ``{"list": [...], "page", "pageSize", "total"}``。
|
|
67
|
+
列表项 ``id`` 即下单 ``good_id``;``inventory==0`` 表示暂无库存。
|
|
68
|
+
|
|
69
|
+
``charging`` 对可筛选接口是必填语义(默认 ``hour``),可选
|
|
70
|
+
``hour`` / ``day`` / ``week`` / ``month`` / ``year``。
|
|
71
|
+
``web=True`` 走官网列表(仅分页,无筛选)。
|
|
72
|
+
"""
|
|
73
|
+
if web:
|
|
74
|
+
return self._post(
|
|
75
|
+
"/common/product/GetWebVmServerList",
|
|
76
|
+
{"page": page, "pageSize": page_size, **extra},
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
body: dict[str, Any] = {
|
|
80
|
+
"page": page,
|
|
81
|
+
"pageSize": page_size,
|
|
82
|
+
"charging": charging,
|
|
83
|
+
**extra,
|
|
84
|
+
}
|
|
85
|
+
if region_id is not None:
|
|
86
|
+
body["region_id"] = region_id
|
|
87
|
+
if gpu_id is not None:
|
|
88
|
+
body["gpu_id"] = gpu_id
|
|
89
|
+
if gpu_number is not None:
|
|
90
|
+
body["gpu_number"] = gpu_number
|
|
91
|
+
if keyword is not None:
|
|
92
|
+
body["keyword"] = keyword
|
|
93
|
+
if server_id is not None:
|
|
94
|
+
body["server_id"] = server_id
|
|
95
|
+
return self._post("/common/product/GetVmServerList", body)
|
|
96
|
+
|
|
97
|
+
def get_offer(self, server_id: int) -> Any:
|
|
98
|
+
"""单个可购买资源详情。"""
|
|
99
|
+
return self._by_id("/common/product/GetVmServerInfo", server_id)
|
|
100
|
+
|
|
101
|
+
def list_gpu_types(self, *, page: int = 1, page_size: int = 100, **extra: Any) -> Any:
|
|
102
|
+
"""云主机可选 GPU 型号(筛选 ``list_offers(gpu_id=...)``)。"""
|
|
103
|
+
return self._post(
|
|
104
|
+
"/common/product/GetVmServerGpuTypeList",
|
|
105
|
+
{"page": page, "pageSize": page_size, **extra},
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
def list_regions(self, *, parent_id: Optional[int] = None, **extra: Any) -> Any:
|
|
109
|
+
"""区域树;传 ``parent_id`` 则查子区域。"""
|
|
110
|
+
if parent_id is None and not extra:
|
|
111
|
+
return self._post("/common/product/GetRegionTree", {})
|
|
112
|
+
body: dict[str, Any] = dict(extra)
|
|
113
|
+
if parent_id is not None:
|
|
114
|
+
body["id"] = parent_id
|
|
115
|
+
return self._post("/common/product/GetRegionChildrenList", body)
|
|
116
|
+
|
|
117
|
+
def list_os_images(
|
|
118
|
+
self,
|
|
119
|
+
*,
|
|
120
|
+
image_os_type: int,
|
|
121
|
+
server_id: Optional[int] = None,
|
|
122
|
+
server_type: int = 2,
|
|
123
|
+
scene: Optional[int] = None,
|
|
124
|
+
**extra: Any,
|
|
125
|
+
) -> Any:
|
|
126
|
+
"""系统镜像列表(购买 / 重装时选 ``image_id``)。
|
|
127
|
+
|
|
128
|
+
``image_os_type``:1 windows / 2 ubuntu。
|
|
129
|
+
``server_type``:1 容器 / 2 虚拟机(云主机用 2)。
|
|
130
|
+
"""
|
|
131
|
+
body: dict[str, Any] = {
|
|
132
|
+
"type": image_os_type,
|
|
133
|
+
"server_type": server_type,
|
|
134
|
+
**extra,
|
|
135
|
+
}
|
|
136
|
+
if server_id is not None:
|
|
137
|
+
body["server_id"] = server_id
|
|
138
|
+
if scene is not None:
|
|
139
|
+
body["scene"] = scene
|
|
140
|
+
return self._post("/common/product/GetOsImageList", body)
|
|
141
|
+
|
|
142
|
+
def list_public_images(self, **extra: Any) -> Any:
|
|
143
|
+
"""公共镜像列表。"""
|
|
144
|
+
return self._post("/common/product/GetPublicImageList", dict(extra))
|
|
145
|
+
|
|
146
|
+
def list_usable_images(
|
|
147
|
+
self,
|
|
148
|
+
*,
|
|
149
|
+
object_id: int,
|
|
150
|
+
object_type: int = ORDER_TYPE_CVM,
|
|
151
|
+
page: int = 1,
|
|
152
|
+
page_size: int = 10,
|
|
153
|
+
keyword: Optional[str] = None,
|
|
154
|
+
**extra: Any,
|
|
155
|
+
) -> Any:
|
|
156
|
+
"""创建实例可用的用户镜像列表。``object_type``:1 容器 / 2 云主机。"""
|
|
157
|
+
body: dict[str, Any] = {
|
|
158
|
+
"object_id": object_id,
|
|
159
|
+
"object_type": object_type,
|
|
160
|
+
"page": page,
|
|
161
|
+
"pageSize": page_size,
|
|
162
|
+
**extra,
|
|
163
|
+
}
|
|
164
|
+
if keyword is not None:
|
|
165
|
+
body["keyword"] = keyword
|
|
166
|
+
return self._post("/user/image/GetUsableImageList", body)
|
|
167
|
+
|
|
168
|
+
# ----- 下单 / 支付 / 续费 -----
|
|
169
|
+
|
|
170
|
+
def create(
|
|
171
|
+
self,
|
|
172
|
+
*,
|
|
173
|
+
good_id: int,
|
|
174
|
+
order_type: int = ORDER_TYPE_CVM,
|
|
175
|
+
image_id: Optional[int] = None,
|
|
176
|
+
image_type: Optional[int] = None,
|
|
177
|
+
charging: Optional[str] = None,
|
|
178
|
+
cycle_number: Optional[int] = None,
|
|
179
|
+
number: Optional[int] = None,
|
|
180
|
+
renew: Optional[bool] = None,
|
|
181
|
+
coupon_id: Optional[int] = None,
|
|
182
|
+
notes: Optional[str] = None,
|
|
183
|
+
sub_type: Optional[int] = None,
|
|
184
|
+
**extra: Any,
|
|
185
|
+
) -> Any:
|
|
186
|
+
"""创建云主机新购订单。``order_type`` 默认 2(云主机)。"""
|
|
187
|
+
body: dict[str, Any] = {
|
|
188
|
+
"good_id": good_id,
|
|
189
|
+
"order_type": order_type,
|
|
190
|
+
**extra,
|
|
191
|
+
}
|
|
192
|
+
if image_id is not None:
|
|
193
|
+
body["image_id"] = image_id
|
|
194
|
+
if image_type is not None:
|
|
195
|
+
body["image_type"] = image_type
|
|
196
|
+
if charging is not None:
|
|
197
|
+
body["charging"] = charging
|
|
198
|
+
if cycle_number is not None:
|
|
199
|
+
body["cycle_number"] = cycle_number
|
|
200
|
+
if number is not None:
|
|
201
|
+
body["number"] = number
|
|
202
|
+
if renew is not None:
|
|
203
|
+
body["renew"] = renew
|
|
204
|
+
if coupon_id is not None:
|
|
205
|
+
body["coupon_id"] = coupon_id
|
|
206
|
+
if notes is not None:
|
|
207
|
+
body["notes"] = notes
|
|
208
|
+
if sub_type is not None:
|
|
209
|
+
body["sub_type"] = sub_type
|
|
210
|
+
return self._post("/user/order/CreateOrder", body)
|
|
211
|
+
|
|
212
|
+
def pay(
|
|
213
|
+
self,
|
|
214
|
+
order_id: int,
|
|
215
|
+
*,
|
|
216
|
+
pay_type: int = 1,
|
|
217
|
+
client: int = 1,
|
|
218
|
+
**extra: Any,
|
|
219
|
+
) -> Any:
|
|
220
|
+
"""支付订单。``pay_type``:1 余额 / 2 微信 / 3 支付宝 / 5 stripe。"""
|
|
221
|
+
return self._post(
|
|
222
|
+
"/user/order/PayOrder",
|
|
223
|
+
{
|
|
224
|
+
"order_id": order_id,
|
|
225
|
+
"pay_type": pay_type,
|
|
226
|
+
"client": client,
|
|
227
|
+
**extra,
|
|
228
|
+
},
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
def create_and_pay(
|
|
232
|
+
self,
|
|
233
|
+
*,
|
|
234
|
+
good_id: int,
|
|
235
|
+
pay_type: int = 1,
|
|
236
|
+
order_id_key: str = "id",
|
|
237
|
+
**create_kwargs: Any,
|
|
238
|
+
) -> dict[str, Any]:
|
|
239
|
+
"""下单并支付,返回 ``{"order": ..., "payment": ...}``。"""
|
|
240
|
+
order = self.create(good_id=good_id, **create_kwargs)
|
|
241
|
+
order_id = None
|
|
242
|
+
if isinstance(order, dict):
|
|
243
|
+
order_id = order.get(order_id_key) or order.get("order_id")
|
|
244
|
+
if order_id is None:
|
|
245
|
+
raise ValueError(
|
|
246
|
+
f"Cannot find order id in create response "
|
|
247
|
+
f"(tried '{order_id_key}' / 'order_id'): {order!r}"
|
|
248
|
+
)
|
|
249
|
+
payment = self.pay(int(order_id), pay_type=pay_type)
|
|
250
|
+
return {"order": order, "payment": payment}
|
|
251
|
+
|
|
252
|
+
def renew(
|
|
253
|
+
self,
|
|
254
|
+
product_id: int,
|
|
255
|
+
*,
|
|
256
|
+
charging: str,
|
|
257
|
+
renew: bool = True,
|
|
258
|
+
order_type: int = ORDER_TYPE_CVM,
|
|
259
|
+
cycle_number: Optional[int] = None,
|
|
260
|
+
coupon_id: Optional[int] = None,
|
|
261
|
+
notes: Optional[str] = None,
|
|
262
|
+
**extra: Any,
|
|
263
|
+
) -> Any:
|
|
264
|
+
"""创建续费订单。``product_id`` 为已购云主机实例 id。"""
|
|
265
|
+
body: dict[str, Any] = {
|
|
266
|
+
"product_id": product_id,
|
|
267
|
+
"charging": charging,
|
|
268
|
+
"renew": renew,
|
|
269
|
+
"order_type": order_type,
|
|
270
|
+
**extra,
|
|
271
|
+
}
|
|
272
|
+
if cycle_number is not None:
|
|
273
|
+
body["cycle_number"] = cycle_number
|
|
274
|
+
if coupon_id is not None:
|
|
275
|
+
body["coupon_id"] = coupon_id
|
|
276
|
+
if notes is not None:
|
|
277
|
+
body["notes"] = notes
|
|
278
|
+
return self._post("/user/order/RenewOrder", body)
|
|
279
|
+
|
|
280
|
+
# ----- 已购实例:列表 / 详情 / 统计 -----
|
|
281
|
+
|
|
282
|
+
def list(
|
|
283
|
+
self,
|
|
284
|
+
*,
|
|
285
|
+
page: int = 1,
|
|
286
|
+
page_size: int = 10,
|
|
287
|
+
status: Optional[int] = None,
|
|
288
|
+
keyword: Optional[str] = None,
|
|
289
|
+
charging: Optional[int] = None,
|
|
290
|
+
gpu_number: Optional[int] = None,
|
|
291
|
+
auto_renew: Optional[int] = None,
|
|
292
|
+
stop_range: Optional[str] = None,
|
|
293
|
+
**extra: Any,
|
|
294
|
+
) -> Any:
|
|
295
|
+
"""已购云主机列表。"""
|
|
296
|
+
body: dict[str, Any] = {"page": page, "pageSize": page_size, **extra}
|
|
297
|
+
if status is not None:
|
|
298
|
+
body["status"] = status
|
|
299
|
+
if keyword is not None:
|
|
300
|
+
body["keyword"] = keyword
|
|
301
|
+
if charging is not None:
|
|
302
|
+
body["charging"] = charging
|
|
303
|
+
if gpu_number is not None:
|
|
304
|
+
body["gpu_number"] = gpu_number
|
|
305
|
+
if auto_renew is not None:
|
|
306
|
+
body["auto_renew"] = auto_renew
|
|
307
|
+
if stop_range is not None:
|
|
308
|
+
body["stop_range"] = stop_range
|
|
309
|
+
return self._post("/user/cvm/GetCvmList", body)
|
|
310
|
+
|
|
311
|
+
def get(self, instance_id: int) -> Any:
|
|
312
|
+
"""云主机详情。"""
|
|
313
|
+
return self._by_id("/user/cvm/GetCvmInfo", instance_id)
|
|
314
|
+
|
|
315
|
+
def statistics(self) -> Any:
|
|
316
|
+
"""云主机统计。"""
|
|
317
|
+
return self._post("/user/cvm/GetCvmStatistics", {})
|
|
318
|
+
|
|
319
|
+
def list_instance_gpu_types(self) -> Any:
|
|
320
|
+
"""已购列表可筛选的 GPU 类型。"""
|
|
321
|
+
return self._post("/user/cvm/GetCvmGpuList", {})
|
|
322
|
+
|
|
323
|
+
def list_cluster(
|
|
324
|
+
self,
|
|
325
|
+
*,
|
|
326
|
+
vm_server_id: Optional[int] = None,
|
|
327
|
+
current_cvm_id: Optional[int] = None,
|
|
328
|
+
**extra: Any,
|
|
329
|
+
) -> Any:
|
|
330
|
+
"""同集群内用户云主机。"""
|
|
331
|
+
body: dict[str, Any] = dict(extra)
|
|
332
|
+
if vm_server_id is not None:
|
|
333
|
+
body["vm_server_id"] = vm_server_id
|
|
334
|
+
if current_cvm_id is not None:
|
|
335
|
+
body["current_cvm_id"] = current_cvm_id
|
|
336
|
+
return self._post("/user/cvm/GetClusterCvm", body)
|
|
337
|
+
|
|
338
|
+
# ----- 电源 / 生命周期 -----
|
|
339
|
+
|
|
340
|
+
def start(self, instance_id: int) -> Any:
|
|
341
|
+
"""开机。"""
|
|
342
|
+
return self._by_id("/user/cvm/Start", instance_id)
|
|
343
|
+
|
|
344
|
+
def stop(self, instance_id: int) -> Any:
|
|
345
|
+
"""关机。"""
|
|
346
|
+
return self._by_id("/user/cvm/Stop", instance_id)
|
|
347
|
+
|
|
348
|
+
def reboot(self, instance_id: int) -> Any:
|
|
349
|
+
"""重启。"""
|
|
350
|
+
return self._by_id("/user/cvm/Restart", instance_id)
|
|
351
|
+
|
|
352
|
+
def delete(self, instance_id: int) -> Any:
|
|
353
|
+
"""删除 / 释放。"""
|
|
354
|
+
return self._by_id("/user/cvm/Delete", instance_id)
|
|
355
|
+
|
|
356
|
+
def reset_system(
|
|
357
|
+
self,
|
|
358
|
+
instance_id: int,
|
|
359
|
+
*,
|
|
360
|
+
image_id: int,
|
|
361
|
+
image_type: int,
|
|
362
|
+
**extra: Any,
|
|
363
|
+
) -> Any:
|
|
364
|
+
"""重置系统。``image_type``:1 基础 / 2 模型 / 3 系统 / 7 拷贝 / 9 用户镜像。"""
|
|
365
|
+
return self._post(
|
|
366
|
+
"/user/cvm/ResetSystem",
|
|
367
|
+
{
|
|
368
|
+
"id": instance_id,
|
|
369
|
+
"image_id": image_id,
|
|
370
|
+
"image_type": image_type,
|
|
371
|
+
**extra,
|
|
372
|
+
},
|
|
373
|
+
)
|
|
374
|
+
|
|
375
|
+
def convert_cardless(self, instance_id: int) -> Any:
|
|
376
|
+
"""转为无卡模式。"""
|
|
377
|
+
return self._by_id("/user/cvm/ConvertCardless", instance_id)
|
|
378
|
+
|
|
379
|
+
def stop_cardless(self, instance_id: int) -> Any:
|
|
380
|
+
"""停止无卡模式。"""
|
|
381
|
+
return self._by_id("/user/cvm/StopCardless", instance_id)
|
|
382
|
+
|
|
383
|
+
def convert_charging(self, instance_id: int, **extra: Any) -> Any:
|
|
384
|
+
"""包年包月转按量计费。"""
|
|
385
|
+
return self._by_id("/user/cvm/ConvertCharging", instance_id, **extra)
|
|
386
|
+
|
|
387
|
+
# ----- 配置变更 -----
|
|
388
|
+
|
|
389
|
+
def edit(self, instance_id: int, *, name: str, **extra: Any) -> Any:
|
|
390
|
+
"""修改云主机名称等信息。"""
|
|
391
|
+
return self._post(
|
|
392
|
+
"/user/cvm/EditCvm",
|
|
393
|
+
{"id": instance_id, "name": name, **extra},
|
|
394
|
+
)
|
|
395
|
+
|
|
396
|
+
def change_password(self, instance_id: int, *, password: str, **extra: Any) -> Any:
|
|
397
|
+
"""修改登录密码。"""
|
|
398
|
+
return self._post(
|
|
399
|
+
"/user/cvm/EditPassword",
|
|
400
|
+
{"id": instance_id, "password": password, **extra},
|
|
401
|
+
)
|
|
402
|
+
|
|
403
|
+
def change_renew(self, instance_id: int, *, mode: int, **extra: Any) -> Any:
|
|
404
|
+
"""修改续费方式。``mode``:1 自动 / 2 手动。"""
|
|
405
|
+
return self._post(
|
|
406
|
+
"/user/cvm/ChangeRenew",
|
|
407
|
+
{"id": instance_id, "mode": mode, **extra},
|
|
408
|
+
)
|
|
409
|
+
|
|
410
|
+
def change_reserve_disk(self, instance_id: int, *, mode: int, **extra: Any) -> Any:
|
|
411
|
+
"""是否保留磁盘。``mode``:1 保留 / 2 不保留。"""
|
|
412
|
+
return self._post(
|
|
413
|
+
"/user/cvm/ChangeReserveDisk",
|
|
414
|
+
{"id": instance_id, "mode": mode, **extra},
|
|
415
|
+
)
|
|
416
|
+
|
|
417
|
+
def expand(
|
|
418
|
+
self,
|
|
419
|
+
instance_id: int,
|
|
420
|
+
*,
|
|
421
|
+
cpu_expansion: Optional[int] = None,
|
|
422
|
+
memory_expansion: Optional[int] = None,
|
|
423
|
+
disk_expansion: Optional[int] = None,
|
|
424
|
+
sys_disk_expansion: Optional[int] = None,
|
|
425
|
+
**extra: Any,
|
|
426
|
+
) -> Any:
|
|
427
|
+
"""云主机扩容。"""
|
|
428
|
+
body: dict[str, Any] = {"id": instance_id, **extra}
|
|
429
|
+
if cpu_expansion is not None:
|
|
430
|
+
body["cpu_expansion"] = cpu_expansion
|
|
431
|
+
if memory_expansion is not None:
|
|
432
|
+
body["memory_expansion"] = memory_expansion
|
|
433
|
+
if disk_expansion is not None:
|
|
434
|
+
body["disk_expansion"] = disk_expansion
|
|
435
|
+
if sys_disk_expansion is not None:
|
|
436
|
+
body["sys_disk_expansion"] = sys_disk_expansion
|
|
437
|
+
return self._post("/user/cvm/CvmExpand", body)
|
|
438
|
+
|
|
439
|
+
def get_expand_amount(
|
|
440
|
+
self,
|
|
441
|
+
instance_id: int,
|
|
442
|
+
*,
|
|
443
|
+
cpu_expansion: Optional[int] = None,
|
|
444
|
+
memory_expansion: Optional[int] = None,
|
|
445
|
+
disk_expansion: Optional[int] = None,
|
|
446
|
+
sys_disk_expansion: Optional[int] = None,
|
|
447
|
+
**extra: Any,
|
|
448
|
+
) -> Any:
|
|
449
|
+
"""查询扩容预计金额。"""
|
|
450
|
+
body: dict[str, Any] = {"id": instance_id, **extra}
|
|
451
|
+
if cpu_expansion is not None:
|
|
452
|
+
body["cpu_expansion"] = cpu_expansion
|
|
453
|
+
if memory_expansion is not None:
|
|
454
|
+
body["memory_expansion"] = memory_expansion
|
|
455
|
+
if disk_expansion is not None:
|
|
456
|
+
body["disk_expansion"] = disk_expansion
|
|
457
|
+
if sys_disk_expansion is not None:
|
|
458
|
+
body["sys_disk_expansion"] = sys_disk_expansion
|
|
459
|
+
return self._post("/user/cvm/GetExpandAmount", body)
|
|
460
|
+
|
|
461
|
+
def get_refund_amount(self, instance_id: int, *, type: int = 1, **extra: Any) -> Any:
|
|
462
|
+
"""预计退款金额。``type``:1 退款 / 2 转按量。"""
|
|
463
|
+
return self._post(
|
|
464
|
+
"/user/cvm/GetRefundAmount",
|
|
465
|
+
{"id": instance_id, "type": type, **extra},
|
|
466
|
+
)
|
|
467
|
+
|
|
468
|
+
def refund(self, instance_id: int) -> Any:
|
|
469
|
+
"""包年包月退款。"""
|
|
470
|
+
return self._by_id("/user/cvm/Refund", instance_id)
|
|
471
|
+
|
|
472
|
+
# ----- 密码 / 密钥 / 软件 -----
|
|
473
|
+
|
|
474
|
+
def get_ssh_password(self, instance_id: int) -> Any:
|
|
475
|
+
"""查看 SSH 密码。"""
|
|
476
|
+
return self._by_id("/user/cvm/CatPasswd", instance_id)
|
|
477
|
+
|
|
478
|
+
def get_vnc_password(self, instance_id: int) -> Any:
|
|
479
|
+
"""查看 VNC 密码。"""
|
|
480
|
+
return self._by_id("/user/cvm/CatVncPasswd", instance_id)
|
|
481
|
+
|
|
482
|
+
def get_rdp_password(self, instance_id: int) -> Any:
|
|
483
|
+
"""查看 RDP 密码。"""
|
|
484
|
+
return self._by_id("/user/cvm/CatRdpPasswd", instance_id)
|
|
485
|
+
|
|
486
|
+
def get_jupyter_password(self, instance_id: int) -> Any:
|
|
487
|
+
"""查看 Jupyter 密码。"""
|
|
488
|
+
return self._by_id("/user/cvm/CatJupyterPasswd", instance_id)
|
|
489
|
+
|
|
490
|
+
def get_vscode_password(self, instance_id: int) -> Any:
|
|
491
|
+
"""查看 VS Code 密码。"""
|
|
492
|
+
return self._by_id("/user/cvm/CatVsCodePasswd", instance_id)
|
|
493
|
+
|
|
494
|
+
def generate_key(self, instance_id: int) -> Any:
|
|
495
|
+
"""生成 SSH 密钥。"""
|
|
496
|
+
return self._by_id("/user/cvm/GenerateKey", instance_id)
|
|
497
|
+
|
|
498
|
+
def download_key(self, instance_id: int) -> Any:
|
|
499
|
+
"""下载 SSH 密钥。"""
|
|
500
|
+
return self._by_id("/user/cvm/DownLoadKey", instance_id)
|
|
501
|
+
|
|
502
|
+
def get_software_config(self, instance_id: int) -> Any:
|
|
503
|
+
"""软件配置详情。"""
|
|
504
|
+
return self._by_id("/user/cvm/GetSoftwareConfig", instance_id)
|
|
505
|
+
|
|
506
|
+
# ----- 端口 / 监控 / 截图 / 镜像 -----
|
|
507
|
+
|
|
508
|
+
def list_ports(self, instance_id: int) -> Any:
|
|
509
|
+
"""端口列表。"""
|
|
510
|
+
return self._by_id("/user/cvm/GetCvmPortList", instance_id)
|
|
511
|
+
|
|
512
|
+
def list_default_ports(self, instance_id: int) -> Any:
|
|
513
|
+
"""默认端口列表。"""
|
|
514
|
+
return self._by_id("/user/cvm/GetCvmDefaultPortList", instance_id)
|
|
515
|
+
|
|
516
|
+
def update_ports(
|
|
517
|
+
self,
|
|
518
|
+
instance_id: int,
|
|
519
|
+
port_list: Sequence[Mapping[str, Any]],
|
|
520
|
+
**extra: Any,
|
|
521
|
+
) -> Any:
|
|
522
|
+
"""更新端口列表。每项可含 ``name`` / ``internal_port`` / ``external_port`` / ``protocol_type``。"""
|
|
523
|
+
return self._post(
|
|
524
|
+
"/user/cvm/UpCvmPortList",
|
|
525
|
+
{"id": instance_id, "port_list": list(port_list), **extra},
|
|
526
|
+
)
|
|
527
|
+
|
|
528
|
+
def get_monitor(
|
|
529
|
+
self,
|
|
530
|
+
instance_id: int,
|
|
531
|
+
*,
|
|
532
|
+
mon_type: int,
|
|
533
|
+
time_type: str,
|
|
534
|
+
time_value: Optional[int] = None,
|
|
535
|
+
**extra: Any,
|
|
536
|
+
) -> Any:
|
|
537
|
+
"""监控数据。
|
|
538
|
+
|
|
539
|
+
``mon_type``:1 cpu / 2 内存 / 3 网络 / 4 硬盘。
|
|
540
|
+
``time_type``:``hour`` / ``day`` / ``month``。
|
|
541
|
+
"""
|
|
542
|
+
body: dict[str, Any] = {
|
|
543
|
+
"id": instance_id,
|
|
544
|
+
"mon_type": mon_type,
|
|
545
|
+
"time_type": time_type,
|
|
546
|
+
**extra,
|
|
547
|
+
}
|
|
548
|
+
if time_value is not None:
|
|
549
|
+
body["time_value"] = time_value
|
|
550
|
+
return self._post("/user/cvm/GetMonitor", body)
|
|
551
|
+
|
|
552
|
+
def take_screenshot(self, instance_id: int) -> Any:
|
|
553
|
+
"""屏幕截图。"""
|
|
554
|
+
return self._by_id("/user/cvm/TakeScreenshot", instance_id)
|
|
555
|
+
|
|
556
|
+
def create_image(self, instance_id: int, *, name: str, **extra: Any) -> Any:
|
|
557
|
+
"""从云主机创建镜像。"""
|
|
558
|
+
return self._post(
|
|
559
|
+
"/user/cvm/CreateImage",
|
|
560
|
+
{"cid": instance_id, "name": name, **extra},
|
|
561
|
+
)
|