ibee-cli 0.1.0__tar.gz → 0.2.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.
- {ibee_cli-0.1.0/src/ibee_cli.egg-info → ibee_cli-0.2.0}/PKG-INFO +36 -6
- ibee_cli-0.2.0/README.md +95 -0
- {ibee_cli-0.1.0 → ibee_cli-0.2.0}/pyproject.toml +2 -2
- ibee_cli-0.2.0/src/ibee_cli/commands/compute.py +95 -0
- ibee_cli-0.2.0/src/ibee_cli/commands/gpus.py +170 -0
- ibee_cli-0.2.0/src/ibee_cli/commands/ops.py +31 -0
- ibee_cli-0.2.0/src/ibee_cli/commands/secrets.py +228 -0
- ibee_cli-0.2.0/src/ibee_cli/commands/vms.py +162 -0
- ibee_cli-0.2.0/src/ibee_cli/helpers.py +106 -0
- {ibee_cli-0.1.0 → ibee_cli-0.2.0}/src/ibee_cli/main.py +3 -1
- {ibee_cli-0.1.0 → ibee_cli-0.2.0/src/ibee_cli.egg-info}/PKG-INFO +36 -6
- {ibee_cli-0.1.0 → ibee_cli-0.2.0}/src/ibee_cli.egg-info/SOURCES.txt +3 -0
- {ibee_cli-0.1.0 → ibee_cli-0.2.0}/src/ibee_cli.egg-info/requires.txt +1 -1
- ibee_cli-0.1.0/README.md +0 -65
- ibee_cli-0.1.0/src/ibee_cli/commands/gpus.py +0 -41
- ibee_cli-0.1.0/src/ibee_cli/commands/secrets.py +0 -72
- ibee_cli-0.1.0/src/ibee_cli/commands/vms.py +0 -84
- {ibee_cli-0.1.0 → ibee_cli-0.2.0}/LICENSE +0 -0
- {ibee_cli-0.1.0 → ibee_cli-0.2.0}/setup.cfg +0 -0
- {ibee_cli-0.1.0 → ibee_cli-0.2.0}/src/ibee_cli/__init__.py +0 -0
- {ibee_cli-0.1.0 → ibee_cli-0.2.0}/src/ibee_cli/commands/__init__.py +0 -0
- {ibee_cli-0.1.0 → ibee_cli-0.2.0}/src/ibee_cli/commands/buckets.py +0 -0
- {ibee_cli-0.1.0 → ibee_cli-0.2.0}/src/ibee_cli/context.py +0 -0
- {ibee_cli-0.1.0 → ibee_cli-0.2.0}/src/ibee_cli/render.py +0 -0
- {ibee_cli-0.1.0 → ibee_cli-0.2.0}/src/ibee_cli.egg-info/dependency_links.txt +0 -0
- {ibee_cli-0.1.0 → ibee_cli-0.2.0}/src/ibee_cli.egg-info/entry_points.txt +0 -0
- {ibee_cli-0.1.0 → ibee_cli-0.2.0}/src/ibee_cli.egg-info/top_level.txt +0 -0
- {ibee_cli-0.1.0 → ibee_cli-0.2.0}/tests/test_cli.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ibee-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Official command-line interface for the IBEE Solutions cloud platform
|
|
5
5
|
Author-email: IBEE Solutions <support@ibee.ai>
|
|
6
6
|
License: MIT
|
|
@@ -14,7 +14,7 @@ Classifier: Environment :: Console
|
|
|
14
14
|
Requires-Python: >=3.10
|
|
15
15
|
Description-Content-Type: text/markdown
|
|
16
16
|
License-File: LICENSE
|
|
17
|
-
Requires-Dist: ibee>=0.1.
|
|
17
|
+
Requires-Dist: ibee>=0.1.3
|
|
18
18
|
Requires-Dist: typer>=0.12
|
|
19
19
|
Requires-Dist: rich>=13.0
|
|
20
20
|
Provides-Extra: dev
|
|
@@ -53,22 +53,52 @@ ibee buckets list
|
|
|
53
53
|
ibee buckets create my-bucket
|
|
54
54
|
ibee buckets delete my-bucket --yes
|
|
55
55
|
|
|
56
|
-
# Secret Store
|
|
57
|
-
ibee secrets stores
|
|
56
|
+
# Secret Store — stores
|
|
57
|
+
ibee secrets stores list
|
|
58
|
+
ibee secrets stores create production-secrets --description "prod"
|
|
59
|
+
ibee secrets stores get STORE_ID
|
|
60
|
+
ibee secrets stores update STORE_ID --name renamed
|
|
61
|
+
ibee secrets stores archive STORE_ID --yes
|
|
62
|
+
|
|
63
|
+
# Secret Store — secrets
|
|
58
64
|
ibee secrets list --store-id STORE_ID
|
|
59
|
-
ibee secrets value
|
|
65
|
+
ibee secrets create --store-id STORE_ID --name db-url --value '{"url":"postgres://..."}'
|
|
66
|
+
ibee secrets get SECRET_ID # metadata
|
|
67
|
+
ibee secrets value SECRET_ID # current value
|
|
68
|
+
ibee secrets set-value SECRET_ID --value '{"url":"postgres://new"}'
|
|
69
|
+
ibee secrets delete SECRET_ID --yes
|
|
70
|
+
|
|
71
|
+
# Compute catalog (discover placement / plans / images for `create`)
|
|
72
|
+
ibee compute sites
|
|
73
|
+
ibee compute plans --vm-type cloud
|
|
74
|
+
ibee compute images --vm-type gpu
|
|
60
75
|
|
|
61
76
|
# Cloud VMs
|
|
62
77
|
ibee vms list
|
|
63
78
|
ibee vms get VM_ID
|
|
79
|
+
ibee vms create web-01 --plan-id PLAN_ID --template-id IMAGE_ID --ssh-key-id KEY_ID --wait
|
|
64
80
|
ibee vms start VM_ID
|
|
65
81
|
ibee vms stop VM_ID
|
|
66
82
|
ibee vms reboot VM_ID
|
|
83
|
+
ibee vms metrics VM_ID
|
|
84
|
+
ibee vms delete VM_ID --yes --wait
|
|
67
85
|
|
|
68
|
-
# GPU VMs
|
|
86
|
+
# GPU VMs (same verbs as Cloud VMs)
|
|
69
87
|
ibee gpus list
|
|
88
|
+
ibee gpus create train-01 --gpu-model A100 --gpu-count 1 --plan-id PLAN_ID --wait
|
|
89
|
+
ibee gpus start VM_ID
|
|
90
|
+
ibee gpus stop VM_ID
|
|
91
|
+
ibee gpus reboot VM_ID
|
|
92
|
+
ibee gpus metrics VM_ID
|
|
93
|
+
ibee gpus delete VM_ID --yes
|
|
94
|
+
|
|
95
|
+
# Async operations — poll a create/delete/power action to completion
|
|
96
|
+
ibee ops get OPERATION_ID --wait
|
|
70
97
|
```
|
|
71
98
|
|
|
99
|
+
Create/delete/power actions are asynchronous; add `--wait` to block until the
|
|
100
|
+
operation finishes, or poll it later with `ibee ops get`.
|
|
101
|
+
|
|
72
102
|
Every command accepts `--json` for raw output:
|
|
73
103
|
|
|
74
104
|
```bash
|
ibee_cli-0.2.0/README.md
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# IBEE Solutions CLI
|
|
2
|
+
|
|
3
|
+
Official command-line interface for the IBEE Solutions cloud platform. Built on the [`ibee` Python SDK](https://github.com/devs-ibee/ibee-python).
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install ibee-cli
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Requires Python 3.10+.
|
|
12
|
+
|
|
13
|
+
## Authentication
|
|
14
|
+
|
|
15
|
+
Create an API token in the portal under **Settings > API Tokens**, then:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
export IBEE_TOKEN="ibee_live_xxxxxxxxxxxx"
|
|
19
|
+
export IBEE_WORKSPACE_ID="710995"
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Both can also be passed per command with `--token` and `--workspace`.
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
# Object storage
|
|
28
|
+
ibee buckets list
|
|
29
|
+
ibee buckets create my-bucket
|
|
30
|
+
ibee buckets delete my-bucket --yes
|
|
31
|
+
|
|
32
|
+
# Secret Store — stores
|
|
33
|
+
ibee secrets stores list
|
|
34
|
+
ibee secrets stores create production-secrets --description "prod"
|
|
35
|
+
ibee secrets stores get STORE_ID
|
|
36
|
+
ibee secrets stores update STORE_ID --name renamed
|
|
37
|
+
ibee secrets stores archive STORE_ID --yes
|
|
38
|
+
|
|
39
|
+
# Secret Store — secrets
|
|
40
|
+
ibee secrets list --store-id STORE_ID
|
|
41
|
+
ibee secrets create --store-id STORE_ID --name db-url --value '{"url":"postgres://..."}'
|
|
42
|
+
ibee secrets get SECRET_ID # metadata
|
|
43
|
+
ibee secrets value SECRET_ID # current value
|
|
44
|
+
ibee secrets set-value SECRET_ID --value '{"url":"postgres://new"}'
|
|
45
|
+
ibee secrets delete SECRET_ID --yes
|
|
46
|
+
|
|
47
|
+
# Compute catalog (discover placement / plans / images for `create`)
|
|
48
|
+
ibee compute sites
|
|
49
|
+
ibee compute plans --vm-type cloud
|
|
50
|
+
ibee compute images --vm-type gpu
|
|
51
|
+
|
|
52
|
+
# Cloud VMs
|
|
53
|
+
ibee vms list
|
|
54
|
+
ibee vms get VM_ID
|
|
55
|
+
ibee vms create web-01 --plan-id PLAN_ID --template-id IMAGE_ID --ssh-key-id KEY_ID --wait
|
|
56
|
+
ibee vms start VM_ID
|
|
57
|
+
ibee vms stop VM_ID
|
|
58
|
+
ibee vms reboot VM_ID
|
|
59
|
+
ibee vms metrics VM_ID
|
|
60
|
+
ibee vms delete VM_ID --yes --wait
|
|
61
|
+
|
|
62
|
+
# GPU VMs (same verbs as Cloud VMs)
|
|
63
|
+
ibee gpus list
|
|
64
|
+
ibee gpus create train-01 --gpu-model A100 --gpu-count 1 --plan-id PLAN_ID --wait
|
|
65
|
+
ibee gpus start VM_ID
|
|
66
|
+
ibee gpus stop VM_ID
|
|
67
|
+
ibee gpus reboot VM_ID
|
|
68
|
+
ibee gpus metrics VM_ID
|
|
69
|
+
ibee gpus delete VM_ID --yes
|
|
70
|
+
|
|
71
|
+
# Async operations — poll a create/delete/power action to completion
|
|
72
|
+
ibee ops get OPERATION_ID --wait
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Create/delete/power actions are asynchronous; add `--wait` to block until the
|
|
76
|
+
operation finishes, or poll it later with `ibee ops get`.
|
|
77
|
+
|
|
78
|
+
Every command accepts `--json` for raw output:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
ibee --json buckets list
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Environments
|
|
85
|
+
|
|
86
|
+
The CLI targets production by default. Use `--dev` (or `IBEE_ENV=dev`) for the development environment, or `--base-url` for a custom endpoint:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
ibee --dev buckets list
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Related
|
|
93
|
+
|
|
94
|
+
- [Python SDK](https://github.com/devs-ibee/ibee-python) — `pip install ibee`
|
|
95
|
+
- [API documentation](https://ibee.ai/docs/api-reference)
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "ibee-cli"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.2.0"
|
|
8
8
|
description = "Official command-line interface for the IBEE Solutions cloud platform"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = {text = "MIT"}
|
|
@@ -19,7 +19,7 @@ classifiers = [
|
|
|
19
19
|
"Environment :: Console",
|
|
20
20
|
]
|
|
21
21
|
dependencies = [
|
|
22
|
-
"ibee>=0.1.
|
|
22
|
+
"ibee>=0.1.3",
|
|
23
23
|
"typer>=0.12",
|
|
24
24
|
"rich>=13.0",
|
|
25
25
|
]
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"""Compute catalog (discovery) commands: sites, plans, images.
|
|
2
|
+
|
|
3
|
+
These back the choices needed to create a VM (`--plan-id`, `--template-id`,
|
|
4
|
+
placement). They call the SDK's ``compute_catalog`` resource when the installed
|
|
5
|
+
``ibee`` exposes it (>= 0.2.0); on older SDKs they fall back to a direct GET
|
|
6
|
+
against the same gateway, so the commands work regardless of SDK version.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from typing import Optional
|
|
12
|
+
|
|
13
|
+
import httpx
|
|
14
|
+
import typer
|
|
15
|
+
from ibee.environment import IbeeEnvironment
|
|
16
|
+
|
|
17
|
+
from ..context import get_client, get_settings, require_token, require_workspace
|
|
18
|
+
from ..render import handle_api_errors, print_json
|
|
19
|
+
|
|
20
|
+
app = typer.Typer(help="Compute catalog: sites, plans, images", no_args_is_help=True)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _base_url(settings) -> str:
|
|
24
|
+
if settings.base_url:
|
|
25
|
+
return settings.base_url.rstrip("/")
|
|
26
|
+
env = IbeeEnvironment.DEVELOPMENT if settings.dev else IbeeEnvironment.DEFAULT
|
|
27
|
+
return env.value.rstrip("/")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _fetch(ctx: typer.Context, sdk_method: str, path: str, filters: dict):
|
|
31
|
+
"""Return catalog data via the SDK method if available, else a direct GET."""
|
|
32
|
+
settings = get_settings(ctx)
|
|
33
|
+
workspace = require_workspace(settings)
|
|
34
|
+
client = get_client(settings)
|
|
35
|
+
params = {k: v for k, v in filters.items() if v is not None}
|
|
36
|
+
|
|
37
|
+
catalog = getattr(client, "compute_catalog", None)
|
|
38
|
+
if catalog is not None:
|
|
39
|
+
return getattr(catalog, sdk_method)(workspace_id=workspace, **params)
|
|
40
|
+
|
|
41
|
+
# Fallback for SDKs without compute_catalog (< 0.2.0): same gateway + auth.
|
|
42
|
+
token = require_token(settings)
|
|
43
|
+
resp = httpx.get(
|
|
44
|
+
f"{_base_url(settings)}/{path}",
|
|
45
|
+
params={"workspace_id": workspace, **params},
|
|
46
|
+
headers={"Authorization": f"Bearer {token}", "accept": "application/json"},
|
|
47
|
+
timeout=30,
|
|
48
|
+
)
|
|
49
|
+
if resp.status_code >= 400:
|
|
50
|
+
typer.secho(f"API error {resp.status_code}: {resp.text[:300]}", fg=typer.colors.RED, err=True)
|
|
51
|
+
raise typer.Exit(code=1)
|
|
52
|
+
return resp.json()
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@app.command("sites")
|
|
56
|
+
@handle_api_errors
|
|
57
|
+
def list_sites(
|
|
58
|
+
ctx: typer.Context,
|
|
59
|
+
region_id: Optional[str] = typer.Option(None, "--region-id", help="Filter by region"),
|
|
60
|
+
country_id: Optional[str] = typer.Option(None, "--country-id", help="Filter by country"),
|
|
61
|
+
) -> None:
|
|
62
|
+
"""List sites where cloud and GPU VMs can be placed."""
|
|
63
|
+
result = _fetch(ctx, "list_compute_sites", "compute/sites",
|
|
64
|
+
{"region_id": region_id, "country_id": country_id})
|
|
65
|
+
print_json(result)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@app.command("plans")
|
|
69
|
+
@handle_api_errors
|
|
70
|
+
def list_plans(
|
|
71
|
+
ctx: typer.Context,
|
|
72
|
+
vm_type: Optional[str] = typer.Option(None, "--vm-type", help="cloud or gpu"),
|
|
73
|
+
site_id: Optional[str] = typer.Option(None, "--site-id", help="Filter by site"),
|
|
74
|
+
currency: Optional[str] = typer.Option(None, "--currency", help="Price currency (e.g. INR, USD)"),
|
|
75
|
+
billing_interval: Optional[str] = typer.Option(None, "--billing-interval", help="e.g. hourly, monthly"),
|
|
76
|
+
) -> None:
|
|
77
|
+
"""List billable plans for cloud or GPU VMs (use the IDs with `vms/gpus create --plan-id`)."""
|
|
78
|
+
result = _fetch(ctx, "list_compute_plans", "compute/plans", {
|
|
79
|
+
"vm_type": vm_type, "site_id": site_id,
|
|
80
|
+
"currency": currency, "billing_interval": billing_interval,
|
|
81
|
+
})
|
|
82
|
+
print_json(result)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@app.command("images")
|
|
86
|
+
@handle_api_errors
|
|
87
|
+
def list_images(
|
|
88
|
+
ctx: typer.Context,
|
|
89
|
+
vm_type: Optional[str] = typer.Option(None, "--vm-type", help="cloud or gpu"),
|
|
90
|
+
currency: Optional[str] = typer.Option(None, "--currency", help="Price currency (e.g. INR, USD)"),
|
|
91
|
+
) -> None:
|
|
92
|
+
"""List OS templates/images (use the IDs with `vms/gpus create --template-id`)."""
|
|
93
|
+
result = _fetch(ctx, "list_compute_images", "compute/images",
|
|
94
|
+
{"vm_type": vm_type, "currency": currency})
|
|
95
|
+
print_json(result)
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
"""GPU VM commands."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import List, Optional
|
|
6
|
+
|
|
7
|
+
import typer
|
|
8
|
+
|
|
9
|
+
from ..context import get_client, get_settings, require_workspace
|
|
10
|
+
from ..helpers import finish_operation, new_idempotency_key
|
|
11
|
+
from ..render import handle_api_errors, print_json, print_table
|
|
12
|
+
|
|
13
|
+
app = typer.Typer(help="GPU VMs", no_args_is_help=True)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@app.command("list")
|
|
17
|
+
@handle_api_errors
|
|
18
|
+
def list_gpu_vms(ctx: typer.Context) -> None:
|
|
19
|
+
"""List GPU VMs in the workspace."""
|
|
20
|
+
settings = get_settings(ctx)
|
|
21
|
+
client = get_client(settings)
|
|
22
|
+
result = client.gpu_vms.list_gpu_vms(workspace_id=require_workspace(settings))
|
|
23
|
+
if settings.as_json:
|
|
24
|
+
print_json(result)
|
|
25
|
+
return
|
|
26
|
+
vms = getattr(result, "items", None) or getattr(result, "vms", None) or []
|
|
27
|
+
print_table(
|
|
28
|
+
"GPU VMs",
|
|
29
|
+
["ID", "Name", "Status", "GPU", "GPUs", "CPU", "RAM (MB)", "Public IP"],
|
|
30
|
+
[
|
|
31
|
+
(
|
|
32
|
+
v.id, v.name, getattr(v.status, "value", v.status),
|
|
33
|
+
getattr(v, "gpu_model", None), getattr(v, "gpu_count", None),
|
|
34
|
+
v.cpu, v.ram_mb, v.public_ip,
|
|
35
|
+
)
|
|
36
|
+
for v in vms
|
|
37
|
+
],
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@app.command("get")
|
|
42
|
+
@handle_api_errors
|
|
43
|
+
def get_gpu_vm(ctx: typer.Context, vm_id: str = typer.Argument(..., help="GPU VM ID")) -> None:
|
|
44
|
+
"""Show one GPU VM."""
|
|
45
|
+
settings = get_settings(ctx)
|
|
46
|
+
client = get_client(settings)
|
|
47
|
+
result = client.gpu_vms.get_gpu_vm(workspace_id=require_workspace(settings), vm_id=vm_id)
|
|
48
|
+
print_json(result)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@app.command("create")
|
|
52
|
+
@handle_api_errors
|
|
53
|
+
def create_gpu_vm(
|
|
54
|
+
ctx: typer.Context,
|
|
55
|
+
name: str = typer.Argument(..., help="Display name for the GPU VM"),
|
|
56
|
+
gpu_model: str = typer.Option(..., "--gpu-model", help="GPU model (A100, H100, L40S, RTX4090)"),
|
|
57
|
+
gpu_count: int = typer.Option(1, "--gpu-count", help="Number of GPUs to attach"),
|
|
58
|
+
os_distro: str = typer.Option("ubuntu", "--os-distro", help="OS distribution"),
|
|
59
|
+
os_type: str = typer.Option("linux", "--os-type", help="OS family (linux, windows)"),
|
|
60
|
+
cpu: int = typer.Option(8, "--cpu", help="vCPUs (fallback when no plan)"),
|
|
61
|
+
ram_mb: int = typer.Option(32768, "--ram-mb", help="RAM in MB (fallback when no plan)"),
|
|
62
|
+
plan_id: Optional[str] = typer.Option(None, "--plan-id", help="GPU plan ID. See `ibee compute plans`."),
|
|
63
|
+
template_id: Optional[str] = typer.Option(None, "--template-id", help="OS template/image ID. See `ibee compute images`."),
|
|
64
|
+
disk_gb: Optional[int] = typer.Option(None, "--disk-gb", help="Root disk size in GB"),
|
|
65
|
+
ssh_key_id: Optional[List[str]] = typer.Option(None, "--ssh-key-id", help="SSH key ID to inject (repeatable)"),
|
|
66
|
+
tag: Optional[List[str]] = typer.Option(None, "--tag", help="Tag (repeatable)"),
|
|
67
|
+
wait: bool = typer.Option(False, "--wait", help="Poll until the VM is provisioned"),
|
|
68
|
+
) -> None:
|
|
69
|
+
"""Create a GPU VM (async — returns an operation)."""
|
|
70
|
+
settings = get_settings(ctx)
|
|
71
|
+
workspace = require_workspace(settings)
|
|
72
|
+
client = get_client(settings)
|
|
73
|
+
result = client.gpu_vms.create_gpu_vm(
|
|
74
|
+
workspace_id=workspace,
|
|
75
|
+
idempotency_key=new_idempotency_key("gpu-create", name),
|
|
76
|
+
name=name,
|
|
77
|
+
os_distro=os_distro,
|
|
78
|
+
os_type=os_type,
|
|
79
|
+
cpu=cpu,
|
|
80
|
+
ram_mb=ram_mb,
|
|
81
|
+
gpu_count=gpu_count,
|
|
82
|
+
gpu_model=gpu_model,
|
|
83
|
+
plan_id=plan_id,
|
|
84
|
+
template_id=template_id,
|
|
85
|
+
disk_gb=disk_gb,
|
|
86
|
+
ssh_key_ids=ssh_key_id or None,
|
|
87
|
+
tags=tag or None,
|
|
88
|
+
)
|
|
89
|
+
finish_operation(settings, client, workspace, result, "Create", name, wait)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@app.command("delete")
|
|
93
|
+
@handle_api_errors
|
|
94
|
+
def delete_gpu_vm(
|
|
95
|
+
ctx: typer.Context,
|
|
96
|
+
vm_id: str = typer.Argument(..., help="GPU VM ID"),
|
|
97
|
+
yes: bool = typer.Option(False, "--yes", "-y", help="Skip confirmation"),
|
|
98
|
+
wait: bool = typer.Option(False, "--wait", help="Poll until the VM is deleted"),
|
|
99
|
+
) -> None:
|
|
100
|
+
"""Delete a GPU VM (async — returns an operation)."""
|
|
101
|
+
settings = get_settings(ctx)
|
|
102
|
+
if not yes:
|
|
103
|
+
typer.confirm(f"Delete GPU VM '{vm_id}'?", abort=True)
|
|
104
|
+
workspace = require_workspace(settings)
|
|
105
|
+
client = get_client(settings)
|
|
106
|
+
result = client.gpu_vms.delete_gpu_vm(
|
|
107
|
+
vm_id=vm_id,
|
|
108
|
+
workspace_id=workspace,
|
|
109
|
+
idempotency_key=new_idempotency_key("gpu-delete", vm_id),
|
|
110
|
+
)
|
|
111
|
+
finish_operation(settings, client, workspace, result, "Delete", vm_id, wait)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
@app.command("metrics")
|
|
115
|
+
@handle_api_errors
|
|
116
|
+
def gpu_metrics(ctx: typer.Context, vm_id: str = typer.Argument(..., help="GPU VM ID")) -> None:
|
|
117
|
+
"""Show current resource-usage metrics for a GPU VM."""
|
|
118
|
+
settings = get_settings(ctx)
|
|
119
|
+
client = get_client(settings)
|
|
120
|
+
# SDK method was renamed get_gpu_vm_metrics -> get_gpu_vm_metrics_overview
|
|
121
|
+
# (ibee 0.2.0); accept either so the CLI works across SDK versions.
|
|
122
|
+
method = getattr(client.gpu_vms, "get_gpu_vm_metrics_overview", None) or client.gpu_vms.get_gpu_vm_metrics
|
|
123
|
+
result = method(workspace_id=require_workspace(settings), vm_id=vm_id)
|
|
124
|
+
print_json(result)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def _power_action(ctx: typer.Context, vm_id: str, action: str, wait: bool) -> None:
|
|
128
|
+
settings = get_settings(ctx)
|
|
129
|
+
workspace = require_workspace(settings)
|
|
130
|
+
client = get_client(settings)
|
|
131
|
+
method = getattr(client.gpu_vms, f"{action}_gpu_vm")
|
|
132
|
+
result = method(
|
|
133
|
+
workspace_id=workspace,
|
|
134
|
+
vm_id=vm_id,
|
|
135
|
+
idempotency_key=new_idempotency_key(action, vm_id),
|
|
136
|
+
)
|
|
137
|
+
finish_operation(settings, client, workspace, result, action.capitalize(), vm_id, wait)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
@app.command("start")
|
|
141
|
+
@handle_api_errors
|
|
142
|
+
def start_gpu_vm(
|
|
143
|
+
ctx: typer.Context,
|
|
144
|
+
vm_id: str = typer.Argument(...),
|
|
145
|
+
wait: bool = typer.Option(False, "--wait", help="Poll until started"),
|
|
146
|
+
) -> None:
|
|
147
|
+
"""Start a GPU VM."""
|
|
148
|
+
_power_action(ctx, vm_id, "start", wait)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
@app.command("stop")
|
|
152
|
+
@handle_api_errors
|
|
153
|
+
def stop_gpu_vm(
|
|
154
|
+
ctx: typer.Context,
|
|
155
|
+
vm_id: str = typer.Argument(...),
|
|
156
|
+
wait: bool = typer.Option(False, "--wait", help="Poll until stopped"),
|
|
157
|
+
) -> None:
|
|
158
|
+
"""Stop a GPU VM."""
|
|
159
|
+
_power_action(ctx, vm_id, "stop", wait)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
@app.command("reboot")
|
|
163
|
+
@handle_api_errors
|
|
164
|
+
def reboot_gpu_vm(
|
|
165
|
+
ctx: typer.Context,
|
|
166
|
+
vm_id: str = typer.Argument(...),
|
|
167
|
+
wait: bool = typer.Option(False, "--wait", help="Poll until rebooted"),
|
|
168
|
+
) -> None:
|
|
169
|
+
"""Reboot a GPU VM."""
|
|
170
|
+
_power_action(ctx, vm_id, "reboot", wait)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""Compute operation (async task) commands."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import typer
|
|
6
|
+
|
|
7
|
+
from ..context import get_client, get_settings, require_workspace
|
|
8
|
+
from ..helpers import wait_for_operation
|
|
9
|
+
from ..render import handle_api_errors, print_json
|
|
10
|
+
|
|
11
|
+
app = typer.Typer(help="Async compute operations (VM create/delete/power)", no_args_is_help=True)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@app.command("get")
|
|
15
|
+
@handle_api_errors
|
|
16
|
+
def get_operation(
|
|
17
|
+
ctx: typer.Context,
|
|
18
|
+
operation_id: str = typer.Argument(..., help="Operation ID from a create/delete/power action"),
|
|
19
|
+
wait: bool = typer.Option(False, "--wait", help="Poll until the operation reaches a terminal state"),
|
|
20
|
+
) -> None:
|
|
21
|
+
"""Show the status of an async compute operation (cloud or GPU VM)."""
|
|
22
|
+
settings = get_settings(ctx)
|
|
23
|
+
workspace = require_workspace(settings)
|
|
24
|
+
client = get_client(settings)
|
|
25
|
+
if wait:
|
|
26
|
+
result = wait_for_operation(client, workspace, operation_id)
|
|
27
|
+
else:
|
|
28
|
+
result = client.cloud_vms.get_compute_operation(
|
|
29
|
+
operation_id=operation_id, workspace_id=workspace
|
|
30
|
+
)
|
|
31
|
+
print_json(result)
|