lightning-sdk 2025.9.30__py3-none-any.whl → 2025.10.14__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.
- lightning_sdk/__init__.py +1 -1
- lightning_sdk/api/cloud_account_api.py +0 -2
- lightning_sdk/api/studio_api.py +8 -0
- lightning_sdk/base_studio.py +23 -12
- lightning_sdk/cli/base_studio/__init__.py +10 -0
- lightning_sdk/cli/base_studio/list.py +45 -0
- lightning_sdk/cli/entrypoint.py +2 -0
- lightning_sdk/cli/groups.py +7 -0
- lightning_sdk/cli/studio/connect.py +117 -22
- lightning_sdk/cli/studio/ssh.py +3 -6
- lightning_sdk/cli/utils/ssh_connection.py +8 -0
- lightning_sdk/job/job.py +5 -0
- lightning_sdk/job/v1.py +8 -0
- lightning_sdk/job/v2.py +8 -0
- lightning_sdk/lightning_cloud/openapi/__init__.py +20 -0
- lightning_sdk/lightning_cloud/openapi/api/__init__.py +1 -0
- lightning_sdk/lightning_cloud/openapi/api/assistants_service_api.py +19 -19
- lightning_sdk/lightning_cloud/openapi/api/cloud_space_service_api.py +105 -0
- lightning_sdk/lightning_cloud/openapi/api/k8_s_cluster_service_api.py +1463 -240
- lightning_sdk/lightning_cloud/openapi/api/sdk_command_history_service_api.py +141 -0
- lightning_sdk/lightning_cloud/openapi/models/__init__.py +19 -0
- lightning_sdk/lightning_cloud/openapi/models/cloudspace_id_visibility_body.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/cluster_id_metrics_body.py +53 -1
- lightning_sdk/lightning_cloud/openapi/models/deployments_id_body.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/externalv1_cloud_space_instance_status.py +53 -1
- lightning_sdk/lightning_cloud/openapi/models/id_transfer_body.py +53 -1
- lightning_sdk/lightning_cloud/openapi/models/incident_id_messages_body.py +149 -0
- lightning_sdk/lightning_cloud/openapi/models/incidents_id_body.py +279 -0
- lightning_sdk/lightning_cloud/openapi/models/messages_message_id_body.py +149 -0
- lightning_sdk/lightning_cloud/openapi/models/project_id_incidents_body.py +279 -0
- lightning_sdk/lightning_cloud/openapi/models/projects_id_body.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/storage_complete_body.py +15 -15
- lightning_sdk/lightning_cloud/openapi/models/v1_cloud_space_transfer_metadata.py +53 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_create_project_request.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_create_sdk_command_history_request.py +253 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_create_sdk_command_history_response.py +97 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_delete_incident_message_response.py +97 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_delete_incident_response.py +97 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_deployment.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_get_cloud_space_transfer_estimate_response.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_group_pod_metrics.py +1241 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_incident.py +539 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_incident_message.py +253 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_incident_type.py +1 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_job.py +53 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_job_spec.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_kai_scheduler_queue_metrics.py +627 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_list_group_pod_metrics_response.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_list_incident_messages_response.py +149 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_list_incidents_response.py +149 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_list_kai_scheduler_queues_metrics_response.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_machine.py +53 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_membership.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_project_membership.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_project_settings.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_resource_visibility.py +1 -27
- lightning_sdk/lightning_cloud/openapi/models/v1_sdk_command_history_severity.py +104 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_sdk_command_history_type.py +104 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_server_alert_type.py +1 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_user_features.py +157 -53
- lightning_sdk/machine.py +0 -1
- lightning_sdk/studio.py +42 -5
- lightning_sdk/utils/progress.py +32 -33
- {lightning_sdk-2025.9.30.dist-info → lightning_sdk-2025.10.14.dist-info}/METADATA +1 -1
- {lightning_sdk-2025.9.30.dist-info → lightning_sdk-2025.10.14.dist-info}/RECORD +69 -47
- {lightning_sdk-2025.9.30.dist-info → lightning_sdk-2025.10.14.dist-info}/LICENSE +0 -0
- {lightning_sdk-2025.9.30.dist-info → lightning_sdk-2025.10.14.dist-info}/WHEEL +0 -0
- {lightning_sdk-2025.9.30.dist-info → lightning_sdk-2025.10.14.dist-info}/entry_points.txt +0 -0
- {lightning_sdk-2025.9.30.dist-info → lightning_sdk-2025.10.14.dist-info}/top_level.txt +0 -0
lightning_sdk/__init__.py
CHANGED
|
@@ -173,8 +173,6 @@ class CloudAccountApi:
|
|
|
173
173
|
return CloudProvider.GCP
|
|
174
174
|
if cloud_account.spec.lambda_labs_v1:
|
|
175
175
|
return CloudProvider.LAMBDA_LABS
|
|
176
|
-
if cloud_account.spec.vultr_v1:
|
|
177
|
-
return CloudProvider.VULTR
|
|
178
176
|
if cloud_account.spec.voltage_park_v1:
|
|
179
177
|
return CloudProvider.VOLTAGE_PARK
|
|
180
178
|
if cloud_account.spec.nebius_v1:
|
lightning_sdk/api/studio_api.py
CHANGED
|
@@ -438,6 +438,14 @@ class StudioApi:
|
|
|
438
438
|
|
|
439
439
|
return response.compute_config.spot
|
|
440
440
|
|
|
441
|
+
def get_public_ip(self, studio_id: str, teamspace_id: str) -> Optional[str]:
|
|
442
|
+
"""Get the public IP address of the Studio."""
|
|
443
|
+
internal_status = self.get_studio_status(studio_id=studio_id, teamspace_id=teamspace_id).in_use
|
|
444
|
+
if internal_status is None:
|
|
445
|
+
return None
|
|
446
|
+
|
|
447
|
+
return internal_status.public_ip_address
|
|
448
|
+
|
|
441
449
|
def _get_machines_for_cloud_account(
|
|
442
450
|
self, teamspace_id: str, cloud_account_id: str, org_id: str
|
|
443
451
|
) -> List[V1ClusterAccelerator]:
|
lightning_sdk/base_studio.py
CHANGED
|
@@ -16,6 +16,8 @@ class BaseStudioInfo:
|
|
|
16
16
|
name: str
|
|
17
17
|
managed_id: str
|
|
18
18
|
description: str
|
|
19
|
+
creator: str
|
|
20
|
+
enabled: bool
|
|
19
21
|
|
|
20
22
|
|
|
21
23
|
class BaseStudio:
|
|
@@ -80,20 +82,29 @@ class BaseStudio:
|
|
|
80
82
|
disabled=disabled,
|
|
81
83
|
)
|
|
82
84
|
|
|
83
|
-
def list(self, managed: bool = True) -> List[BaseStudioInfo]:
|
|
85
|
+
def list(self, managed: bool = True, include_disabled: bool = False) -> List[BaseStudioInfo]:
|
|
84
86
|
"""List all base studios in the organization.
|
|
85
87
|
|
|
88
|
+
Args:
|
|
89
|
+
managed: Whether to filter for managed base studios.
|
|
90
|
+
include_disabled: Whether to include disabled base studios in the results.
|
|
91
|
+
|
|
86
92
|
Returns:
|
|
87
|
-
List[
|
|
93
|
+
List[BaseStudioInfo]: A list of base studio templates.
|
|
88
94
|
"""
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
95
|
+
templates = self._base_studio_api.get_all_base_studios(self._org.id, managed).templates
|
|
96
|
+
|
|
97
|
+
return [
|
|
98
|
+
BaseStudioInfo(
|
|
99
|
+
id=template.id,
|
|
100
|
+
name=template.name,
|
|
101
|
+
managed_id=template.managed_id,
|
|
102
|
+
description=template.description,
|
|
103
|
+
creator="⚡ Lightning AI"
|
|
104
|
+
if template.managed_id
|
|
105
|
+
else UserApi()._get_user_by_id(template.user_id).username,
|
|
106
|
+
enabled=not template.disabled,
|
|
98
107
|
)
|
|
99
|
-
|
|
108
|
+
for template in templates
|
|
109
|
+
if include_disabled or not template.disabled
|
|
110
|
+
]
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""Base Studio CLI commands."""
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def register_commands(group: click.Group) -> None:
|
|
7
|
+
"""Register base studio commands with the given group."""
|
|
8
|
+
from lightning_sdk.cli.base_studio.list import list_base_studios
|
|
9
|
+
|
|
10
|
+
group.add_command(list_base_studios)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""Base Studio list command."""
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
from rich.table import Table
|
|
5
|
+
|
|
6
|
+
from lightning_sdk.base_studio import BaseStudio
|
|
7
|
+
from lightning_sdk.cli.utils.richt_print import rich_to_str
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@click.command("list")
|
|
11
|
+
@click.option("--include-disabled", help="Include disabled Base Studios in the list.", is_flag=True)
|
|
12
|
+
def list_base_studios(include_disabled: bool) -> None:
|
|
13
|
+
"""List Base Studios in an org.
|
|
14
|
+
|
|
15
|
+
Example:
|
|
16
|
+
lightning base-studio list
|
|
17
|
+
|
|
18
|
+
"""
|
|
19
|
+
return list_impl(include_disabled=include_disabled)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def list_impl(include_disabled: bool) -> None:
|
|
23
|
+
base_studio_cls = BaseStudio()
|
|
24
|
+
base_studios = base_studio_cls.list(include_disabled=include_disabled) + base_studio_cls.list(
|
|
25
|
+
managed=False, include_disabled=include_disabled
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
table = Table(
|
|
29
|
+
pad_edge=True,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
table.add_column("Name")
|
|
33
|
+
table.add_column("Description")
|
|
34
|
+
table.add_column("Creator")
|
|
35
|
+
table.add_column("Enabled")
|
|
36
|
+
|
|
37
|
+
for base_studio in base_studios:
|
|
38
|
+
table.add_row(
|
|
39
|
+
base_studio.name.lower().replace(" ", "-"),
|
|
40
|
+
base_studio.description or "",
|
|
41
|
+
base_studio.creator,
|
|
42
|
+
"Yes" if base_studio.enabled else "No",
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
click.echo(rich_to_str(table), color=True)
|
lightning_sdk/cli/entrypoint.py
CHANGED
|
@@ -17,6 +17,7 @@ from lightning_sdk.api.studio_api import _cloud_url
|
|
|
17
17
|
|
|
18
18
|
# Import legacy groups directly from groups.py
|
|
19
19
|
from lightning_sdk.cli.groups import (
|
|
20
|
+
base_studio,
|
|
20
21
|
config,
|
|
21
22
|
# job,
|
|
22
23
|
# mmt,
|
|
@@ -85,6 +86,7 @@ main_cli.add_command(config)
|
|
|
85
86
|
# main_cli.add_command(mmt)
|
|
86
87
|
main_cli.add_command(studio)
|
|
87
88
|
main_cli.add_command(vm)
|
|
89
|
+
main_cli.add_command(base_studio)
|
|
88
90
|
if os.environ.get("LIGHTNING_EXPERIMENTAL_CLI_ONLY", "0") != "1":
|
|
89
91
|
#### LEGACY COMMANDS ####
|
|
90
92
|
# these commands are currently supported for backwards compatibility, but will potentially be removed in the future.
|
lightning_sdk/cli/groups.py
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import click
|
|
4
4
|
|
|
5
|
+
from lightning_sdk.cli.base_studio import register_commands as register_base_studio_commands
|
|
5
6
|
from lightning_sdk.cli.config import register_commands as register_config_commands
|
|
6
7
|
from lightning_sdk.cli.job import register_commands as register_job_commands
|
|
7
8
|
from lightning_sdk.cli.mmt import register_commands as register_mmt_commands
|
|
@@ -34,9 +35,15 @@ def vm() -> None:
|
|
|
34
35
|
"""Manage Lightning AI VMs."""
|
|
35
36
|
|
|
36
37
|
|
|
38
|
+
@click.group(name="base-studio")
|
|
39
|
+
def base_studio() -> None:
|
|
40
|
+
"""Manage Lightning AI Base Studios."""
|
|
41
|
+
|
|
42
|
+
|
|
37
43
|
# Register config commands with the main config group
|
|
38
44
|
register_job_commands(job)
|
|
39
45
|
register_mmt_commands(mmt)
|
|
40
46
|
register_studio_commands(studio)
|
|
41
47
|
register_config_commands(config)
|
|
42
48
|
register_vm_commands(vm)
|
|
49
|
+
register_base_studio_commands(base_studio)
|
|
@@ -2,20 +2,96 @@
|
|
|
2
2
|
|
|
3
3
|
import subprocess
|
|
4
4
|
import sys
|
|
5
|
-
from typing import Optional
|
|
5
|
+
from typing import Dict, Optional, Set
|
|
6
6
|
|
|
7
7
|
import click
|
|
8
8
|
|
|
9
|
+
from lightning_sdk.base_studio import BaseStudio
|
|
9
10
|
from lightning_sdk.cli.utils.richt_print import studio_name_link
|
|
10
11
|
from lightning_sdk.cli.utils.save_to_config import save_studio_to_config, save_teamspace_to_config
|
|
11
|
-
from lightning_sdk.cli.utils.ssh_connection import
|
|
12
|
+
from lightning_sdk.cli.utils.ssh_connection import configure_ssh_internal
|
|
12
13
|
from lightning_sdk.cli.utils.teamspace_selection import TeamspacesMenu
|
|
13
|
-
from lightning_sdk.lightning_cloud.login import Auth
|
|
14
14
|
from lightning_sdk.lightning_cloud.openapi.rest import ApiException
|
|
15
|
-
from lightning_sdk.machine import CloudProvider
|
|
15
|
+
from lightning_sdk.machine import CloudProvider, Machine
|
|
16
16
|
from lightning_sdk.studio import Studio
|
|
17
17
|
from lightning_sdk.utils.names import random_unique_name
|
|
18
18
|
|
|
19
|
+
DEFAULT_MACHINE = "CPU"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _split_gpus_spec(gpus: str) -> tuple[str, int]:
|
|
23
|
+
machine_name, machine_val = gpus.split(":", 1)
|
|
24
|
+
machine_name = machine_name.strip()
|
|
25
|
+
machine_val = machine_val.strip()
|
|
26
|
+
|
|
27
|
+
if not machine_val.isdigit() or int(machine_val) <= 0:
|
|
28
|
+
raise ValueError(f"Invalid GPU count '{machine_val}'. Must be a positive integer.")
|
|
29
|
+
|
|
30
|
+
machine_num = int(machine_val)
|
|
31
|
+
return machine_name, machine_num
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _construct_available_gpus(machine_options: Dict[str, str]) -> Set[str]:
|
|
35
|
+
# returns available gpus:count
|
|
36
|
+
available_gpus = set()
|
|
37
|
+
for v in machine_options.values():
|
|
38
|
+
if "_X_" in v:
|
|
39
|
+
gpu_type_num = v.replace("_X_", ":")
|
|
40
|
+
available_gpus.add(gpu_type_num)
|
|
41
|
+
else:
|
|
42
|
+
available_gpus.add(v)
|
|
43
|
+
return available_gpus
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _get_machine_from_gpus(gpus: str) -> Machine:
|
|
47
|
+
machine_name = gpus
|
|
48
|
+
machine_num = 1
|
|
49
|
+
|
|
50
|
+
if ":" in gpus:
|
|
51
|
+
machine_name, machine_num = _split_gpus_spec(gpus)
|
|
52
|
+
|
|
53
|
+
machine_options = {
|
|
54
|
+
m.name.lower(): m.name for m in Machine.__dict__.values() if isinstance(m, Machine) and m._include_in_cli
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if machine_num == 1:
|
|
58
|
+
# e.g. gpus=L4 or gpus=L4:1
|
|
59
|
+
gpu_key = machine_name.lower()
|
|
60
|
+
try:
|
|
61
|
+
return machine_options[gpu_key]
|
|
62
|
+
except KeyError:
|
|
63
|
+
available = ", ".join(_construct_available_gpus(machine_options))
|
|
64
|
+
raise ValueError(f"Invalid GPU type '{machine_name}'. Available options: {available}") from None
|
|
65
|
+
|
|
66
|
+
# Else: e.g. gpus=L4:4
|
|
67
|
+
gpu_key = f"{machine_name.lower()}_x_{machine_num}"
|
|
68
|
+
try:
|
|
69
|
+
return machine_options[gpu_key]
|
|
70
|
+
except KeyError:
|
|
71
|
+
available = ", ".join(_construct_available_gpus(machine_options))
|
|
72
|
+
raise ValueError(f"Invalid GPU configuration '{gpus}'. Available options: {available}") from None
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _get_base_studio_id(studio_type: Optional[str]) -> Optional[str]:
|
|
76
|
+
base_studios = BaseStudio()
|
|
77
|
+
base_studios = base_studios.list()
|
|
78
|
+
template_id = None
|
|
79
|
+
|
|
80
|
+
if base_studios and len(base_studios):
|
|
81
|
+
# if not specified by user, use the first existing template studio
|
|
82
|
+
template_id = base_studios[0].id
|
|
83
|
+
# else, try to match the provided studio_type to base studio name
|
|
84
|
+
if studio_type:
|
|
85
|
+
normalized_studio_type = studio_type.lower().replace(" ", "-")
|
|
86
|
+
match = next(
|
|
87
|
+
(s for s in base_studios if s.name.lower().replace(" ", "-") == normalized_studio_type),
|
|
88
|
+
None,
|
|
89
|
+
)
|
|
90
|
+
if match:
|
|
91
|
+
template_id = match.id
|
|
92
|
+
|
|
93
|
+
return template_id
|
|
94
|
+
|
|
19
95
|
|
|
20
96
|
@click.command("connect")
|
|
21
97
|
@click.argument("name", required=False)
|
|
@@ -30,13 +106,32 @@ from lightning_sdk.utils.names import random_unique_name
|
|
|
30
106
|
help="The cloud account to create the studio on. Defaults to teamspace default.",
|
|
31
107
|
type=click.STRING,
|
|
32
108
|
)
|
|
33
|
-
@click.option(
|
|
109
|
+
@click.option(
|
|
110
|
+
"--machine",
|
|
111
|
+
help="The machine type to start the studio on. Defaults to CPU-4",
|
|
112
|
+
type=click.Choice(m.name for m in Machine.__dict__.values() if isinstance(m, Machine) and m._include_in_cli),
|
|
113
|
+
)
|
|
114
|
+
@click.option(
|
|
115
|
+
"--gpus",
|
|
116
|
+
help="The number and type of GPUs to start the studio on (format: TYPE:COUNT, e.g. L4:4)",
|
|
117
|
+
type=click.STRING,
|
|
118
|
+
)
|
|
119
|
+
@click.option(
|
|
120
|
+
"--studio-type",
|
|
121
|
+
help="The base studio template name to use for creating the studio. "
|
|
122
|
+
"Must be lowercase and hyphenated (use '-' instead of spaces). "
|
|
123
|
+
"Run 'lightning base-studio list' to see all available templates. "
|
|
124
|
+
"Defaults to the first available template.",
|
|
125
|
+
type=click.STRING,
|
|
126
|
+
)
|
|
34
127
|
def connect_studio(
|
|
35
128
|
name: Optional[str] = None,
|
|
36
129
|
teamspace: Optional[str] = None,
|
|
37
130
|
cloud_provider: Optional[str] = None,
|
|
38
131
|
cloud_account: Optional[str] = None,
|
|
39
|
-
|
|
132
|
+
machine: Optional[str] = None,
|
|
133
|
+
gpus: Optional[str] = None,
|
|
134
|
+
studio_type: Optional[str] = None,
|
|
40
135
|
) -> None:
|
|
41
136
|
"""Connect to a Studio.
|
|
42
137
|
|
|
@@ -53,6 +148,9 @@ def connect_studio(
|
|
|
53
148
|
|
|
54
149
|
name = name or random_unique_name()
|
|
55
150
|
|
|
151
|
+
# check for available base studios
|
|
152
|
+
template_id = _get_base_studio_id(studio_type)
|
|
153
|
+
|
|
56
154
|
try:
|
|
57
155
|
studio = Studio(
|
|
58
156
|
name=name,
|
|
@@ -60,35 +158,32 @@ def connect_studio(
|
|
|
60
158
|
create_ok=True,
|
|
61
159
|
cloud_provider=cloud_provider,
|
|
62
160
|
cloud_account=cloud_account,
|
|
161
|
+
studio_type=template_id,
|
|
63
162
|
)
|
|
64
163
|
except (RuntimeError, ValueError, ApiException):
|
|
65
164
|
raise ValueError(f"Could not create Studio: '{name}'") from None
|
|
66
165
|
|
|
67
166
|
click.echo(f"Connecting to Studio '{studio_name_link(studio)}' ...")
|
|
68
167
|
|
|
69
|
-
machine = "CPU"
|
|
70
168
|
Studio.show_progress = True
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
169
|
+
|
|
170
|
+
if machine and gpus:
|
|
171
|
+
raise click.UsageError("Options --machine and --gpu are mutually exclusive. Provide only one.")
|
|
172
|
+
elif gpus:
|
|
173
|
+
machine = _get_machine_from_gpus(gpus.strip())
|
|
174
|
+
elif not machine:
|
|
175
|
+
machine = DEFAULT_MACHINE
|
|
74
176
|
|
|
75
177
|
save_studio_to_config(studio)
|
|
76
|
-
|
|
178
|
+
# by default, interruptible is False
|
|
179
|
+
studio.start(machine=machine, interruptible=False)
|
|
77
180
|
|
|
78
|
-
ssh_private_key_path =
|
|
181
|
+
ssh_private_key_path = configure_ssh_internal()
|
|
79
182
|
|
|
183
|
+
ssh_option = "-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o LogLevel=ERROR"
|
|
80
184
|
try:
|
|
81
|
-
ssh_command =
|
|
82
|
-
f"ssh -i {ssh_private_key_path} -o UserKnownHostsFile=/dev/null s_{studio._studio.id}@ssh.lightning.ai"
|
|
83
|
-
)
|
|
185
|
+
ssh_command = f"ssh -i {ssh_private_key_path} {ssh_option} s_{studio._studio.id}@ssh.lightning.ai"
|
|
84
186
|
subprocess.run(ssh_command.split())
|
|
85
187
|
except Exception as ex:
|
|
86
188
|
print(f"Failed to establish SSH connection: {ex}")
|
|
87
189
|
sys.exit(1)
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
def _configure_ssh_internal() -> str:
|
|
91
|
-
"""Internal function to configure SSH without Click decorators."""
|
|
92
|
-
auth = Auth()
|
|
93
|
-
auth.authenticate()
|
|
94
|
-
return download_ssh_keys(auth.api_key, force_download=False)
|
lightning_sdk/cli/studio/ssh.py
CHANGED
|
@@ -6,10 +6,9 @@ from typing import List, Optional
|
|
|
6
6
|
import click
|
|
7
7
|
|
|
8
8
|
from lightning_sdk.cli.utils.save_to_config import save_studio_to_config
|
|
9
|
-
from lightning_sdk.cli.utils.ssh_connection import
|
|
9
|
+
from lightning_sdk.cli.utils.ssh_connection import configure_ssh_internal
|
|
10
10
|
from lightning_sdk.cli.utils.studio_selection import StudiosMenu
|
|
11
11
|
from lightning_sdk.cli.utils.teamspace_selection import TeamspacesMenu
|
|
12
|
-
from lightning_sdk.lightning_cloud.login import Auth
|
|
13
12
|
|
|
14
13
|
|
|
15
14
|
@click.command("ssh")
|
|
@@ -39,9 +38,7 @@ def ssh_studio(name: Optional[str] = None, teamspace: Optional[str] = None, opti
|
|
|
39
38
|
|
|
40
39
|
|
|
41
40
|
def ssh_impl(name: Optional[str], teamspace: Optional[str], option: Optional[List[str]], vm: bool) -> None:
|
|
42
|
-
|
|
43
|
-
auth.authenticate()
|
|
44
|
-
ssh_private_key_path = download_ssh_keys(auth.api_key, force_download=False)
|
|
41
|
+
ssh_private_key_path = configure_ssh_internal()
|
|
45
42
|
|
|
46
43
|
menu = TeamspacesMenu()
|
|
47
44
|
resolved_teamspace = menu(teamspace=teamspace)
|
|
@@ -59,7 +56,7 @@ def ssh_impl(name: Optional[str], teamspace: Optional[str], option: Optional[Lis
|
|
|
59
56
|
subprocess.run(ssh_command.split())
|
|
60
57
|
except Exception:
|
|
61
58
|
# redownload the keys to be sure they are up to date
|
|
62
|
-
|
|
59
|
+
ssh_private_key_path = configure_ssh_internal(force_download=True)
|
|
63
60
|
try:
|
|
64
61
|
subprocess.run(ssh_command.split())
|
|
65
62
|
except Exception:
|
|
@@ -4,9 +4,17 @@ import uuid
|
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
from typing import Optional
|
|
6
6
|
|
|
7
|
+
from lightning_sdk.lightning_cloud.login import Auth
|
|
7
8
|
from lightning_sdk.utils.config import _DEFAULT_CONFIG_FILE_PATH
|
|
8
9
|
|
|
9
10
|
|
|
11
|
+
def configure_ssh_internal(force_download: bool = False) -> str:
|
|
12
|
+
"""Internal function to configure SSH without Click decorators."""
|
|
13
|
+
auth = Auth()
|
|
14
|
+
auth.authenticate()
|
|
15
|
+
return download_ssh_keys(auth.api_key, force_download=force_download)
|
|
16
|
+
|
|
17
|
+
|
|
10
18
|
def download_ssh_keys(
|
|
11
19
|
api_key: str | None,
|
|
12
20
|
force_download: bool = False,
|
lightning_sdk/job/job.py
CHANGED
|
@@ -268,6 +268,11 @@ class Job(_BaseJob):
|
|
|
268
268
|
"""The machine type the job is running on."""
|
|
269
269
|
return self._internal_job.machine
|
|
270
270
|
|
|
271
|
+
@property
|
|
272
|
+
def public_ip(self) -> Optional[str]:
|
|
273
|
+
"""The public IP address of the machine the job is running on."""
|
|
274
|
+
return self._internal_job.public_ip
|
|
275
|
+
|
|
271
276
|
@property
|
|
272
277
|
def artifact_path(self) -> Optional[str]:
|
|
273
278
|
"""Path to the artifacts created by the job within the distributed teamspace filesystem."""
|
lightning_sdk/job/v1.py
CHANGED
|
@@ -181,6 +181,14 @@ class _JobV1(_BaseJob):
|
|
|
181
181
|
"""Get the machine the job is running on."""
|
|
182
182
|
return self.work.machine
|
|
183
183
|
|
|
184
|
+
@property
|
|
185
|
+
def public_ip(self) -> Optional[str]:
|
|
186
|
+
"""Get the public IP of the machine the job is running on."""
|
|
187
|
+
try:
|
|
188
|
+
return self._job.status.ip_address
|
|
189
|
+
except AttributeError:
|
|
190
|
+
return None
|
|
191
|
+
|
|
184
192
|
@property
|
|
185
193
|
def name(self) -> str:
|
|
186
194
|
"""The name of the job."""
|
lightning_sdk/job/v2.py
CHANGED
|
@@ -173,6 +173,14 @@ class _JobV2(_BaseJob):
|
|
|
173
173
|
_get_org_id(self.teamspace),
|
|
174
174
|
)
|
|
175
175
|
|
|
176
|
+
@property
|
|
177
|
+
def public_ip(self) -> Optional[str]:
|
|
178
|
+
"""Get the public IP of the machine the job is running on."""
|
|
179
|
+
try:
|
|
180
|
+
return self._job.public_ip_address
|
|
181
|
+
except AttributeError:
|
|
182
|
+
return None
|
|
183
|
+
|
|
176
184
|
@property
|
|
177
185
|
def artifact_path(self) -> Optional[str]:
|
|
178
186
|
"""The path to the artifacts of the job within the distributed teamspace filesystem."""
|
|
@@ -56,6 +56,7 @@ from lightning_sdk.lightning_cloud.openapi.api.product_license_service_api impor
|
|
|
56
56
|
from lightning_sdk.lightning_cloud.openapi.api.profiler_service_api import ProfilerServiceApi
|
|
57
57
|
from lightning_sdk.lightning_cloud.openapi.api.projects_service_api import ProjectsServiceApi
|
|
58
58
|
from lightning_sdk.lightning_cloud.openapi.api.quest_service_api import QuestServiceApi
|
|
59
|
+
from lightning_sdk.lightning_cloud.openapi.api.sdk_command_history_service_api import SDKCommandHistoryServiceApi
|
|
59
60
|
from lightning_sdk.lightning_cloud.openapi.api.ssh_public_key_service_api import SSHPublicKeyServiceApi
|
|
60
61
|
from lightning_sdk.lightning_cloud.openapi.api.schedules_service_api import SchedulesServiceApi
|
|
61
62
|
from lightning_sdk.lightning_cloud.openapi.api.secret_service_api import SecretServiceApi
|
|
@@ -157,6 +158,8 @@ from lightning_sdk.lightning_cloud.openapi.models.id_uploads_body import IdUploa
|
|
|
157
158
|
from lightning_sdk.lightning_cloud.openapi.models.id_visibility_body import IdVisibilityBody
|
|
158
159
|
from lightning_sdk.lightning_cloud.openapi.models.id_visibility_body1 import IdVisibilityBody1
|
|
159
160
|
from lightning_sdk.lightning_cloud.openapi.models.id_visibility_body2 import IdVisibilityBody2
|
|
161
|
+
from lightning_sdk.lightning_cloud.openapi.models.incident_id_messages_body import IncidentIdMessagesBody
|
|
162
|
+
from lightning_sdk.lightning_cloud.openapi.models.incidents_id_body import IncidentsIdBody
|
|
160
163
|
from lightning_sdk.lightning_cloud.openapi.models.job_id_reportroutingtelemetry_body import JobIdReportroutingtelemetryBody
|
|
161
164
|
from lightning_sdk.lightning_cloud.openapi.models.jobs_id_body import JobsIdBody
|
|
162
165
|
from lightning_sdk.lightning_cloud.openapi.models.jobs_id_body1 import JobsIdBody1
|
|
@@ -169,6 +172,7 @@ from lightning_sdk.lightning_cloud.openapi.models.litpages_id_body import Litpag
|
|
|
169
172
|
from lightning_sdk.lightning_cloud.openapi.models.litregistry_lit_repo_name_body import LitregistryLitRepoNameBody
|
|
170
173
|
from lightning_sdk.lightning_cloud.openapi.models.loggermetrics_id_body import LoggermetricsIdBody
|
|
171
174
|
from lightning_sdk.lightning_cloud.openapi.models.messages_id_body import MessagesIdBody
|
|
175
|
+
from lightning_sdk.lightning_cloud.openapi.models.messages_message_id_body import MessagesMessageIdBody
|
|
172
176
|
from lightning_sdk.lightning_cloud.openapi.models.metrics_stream_id_loggerartifacts_body import MetricsStreamIdLoggerartifactsBody
|
|
173
177
|
from lightning_sdk.lightning_cloud.openapi.models.metricsstream_create_body import MetricsstreamCreateBody
|
|
174
178
|
from lightning_sdk.lightning_cloud.openapi.models.metricsstream_delete_body import MetricsstreamDeleteBody
|
|
@@ -195,6 +199,7 @@ from lightning_sdk.lightning_cloud.openapi.models.project_id_clusters_body impor
|
|
|
195
199
|
from lightning_sdk.lightning_cloud.openapi.models.project_id_datasets_body import ProjectIdDatasetsBody
|
|
196
200
|
from lightning_sdk.lightning_cloud.openapi.models.project_id_endpoints_body import ProjectIdEndpointsBody
|
|
197
201
|
from lightning_sdk.lightning_cloud.openapi.models.project_id_getapp_body import ProjectIdGetappBody
|
|
202
|
+
from lightning_sdk.lightning_cloud.openapi.models.project_id_incidents_body import ProjectIdIncidentsBody
|
|
198
203
|
from lightning_sdk.lightning_cloud.openapi.models.project_id_invite_body import ProjectIdInviteBody
|
|
199
204
|
from lightning_sdk.lightning_cloud.openapi.models.project_id_jobs_body import ProjectIdJobsBody
|
|
200
205
|
from lightning_sdk.lightning_cloud.openapi.models.project_id_kubernetestemplates_body import ProjectIdKubernetestemplatesBody
|
|
@@ -400,6 +405,8 @@ from lightning_sdk.lightning_cloud.openapi.models.v1_create_multi_part_upload_re
|
|
|
400
405
|
from lightning_sdk.lightning_cloud.openapi.models.v1_create_organization_request import V1CreateOrganizationRequest
|
|
401
406
|
from lightning_sdk.lightning_cloud.openapi.models.v1_create_pipeline_template_request import V1CreatePipelineTemplateRequest
|
|
402
407
|
from lightning_sdk.lightning_cloud.openapi.models.v1_create_project_request import V1CreateProjectRequest
|
|
408
|
+
from lightning_sdk.lightning_cloud.openapi.models.v1_create_sdk_command_history_request import V1CreateSDKCommandHistoryRequest
|
|
409
|
+
from lightning_sdk.lightning_cloud.openapi.models.v1_create_sdk_command_history_response import V1CreateSDKCommandHistoryResponse
|
|
403
410
|
from lightning_sdk.lightning_cloud.openapi.models.v1_create_ssh_public_key_request import V1CreateSSHPublicKeyRequest
|
|
404
411
|
from lightning_sdk.lightning_cloud.openapi.models.v1_create_server_alert_response import V1CreateServerAlertResponse
|
|
405
412
|
from lightning_sdk.lightning_cloud.openapi.models.v1_create_shared_metrics_stream_request import V1CreateSharedMetricsStreamRequest
|
|
@@ -440,6 +447,8 @@ from lightning_sdk.lightning_cloud.openapi.models.v1_delete_deployment_release_r
|
|
|
440
447
|
from lightning_sdk.lightning_cloud.openapi.models.v1_delete_deployment_response import V1DeleteDeploymentResponse
|
|
441
448
|
from lightning_sdk.lightning_cloud.openapi.models.v1_delete_endpoint_response import V1DeleteEndpointResponse
|
|
442
449
|
from lightning_sdk.lightning_cloud.openapi.models.v1_delete_git_credentials_response import V1DeleteGitCredentialsResponse
|
|
450
|
+
from lightning_sdk.lightning_cloud.openapi.models.v1_delete_incident_message_response import V1DeleteIncidentMessageResponse
|
|
451
|
+
from lightning_sdk.lightning_cloud.openapi.models.v1_delete_incident_response import V1DeleteIncidentResponse
|
|
443
452
|
from lightning_sdk.lightning_cloud.openapi.models.v1_delete_index_response import V1DeleteIndexResponse
|
|
444
453
|
from lightning_sdk.lightning_cloud.openapi.models.v1_delete_job_response import V1DeleteJobResponse
|
|
445
454
|
from lightning_sdk.lightning_cloud.openapi.models.v1_delete_kubernetes_template_response import V1DeleteKubernetesTemplateResponse
|
|
@@ -567,6 +576,7 @@ from lightning_sdk.lightning_cloud.openapi.models.v1_get_cloud_space_instance_st
|
|
|
567
576
|
from lightning_sdk.lightning_cloud.openapi.models.v1_get_cloud_space_instance_system_metrics_aggregate_response import V1GetCloudSpaceInstanceSystemMetricsAggregateResponse
|
|
568
577
|
from lightning_sdk.lightning_cloud.openapi.models.v1_get_cloud_space_required_balance_status_response import V1GetCloudSpaceRequiredBalanceStatusResponse
|
|
569
578
|
from lightning_sdk.lightning_cloud.openapi.models.v1_get_cloud_space_size_response import V1GetCloudSpaceSizeResponse
|
|
579
|
+
from lightning_sdk.lightning_cloud.openapi.models.v1_get_cloud_space_transfer_estimate_response import V1GetCloudSpaceTransferEstimateResponse
|
|
570
580
|
from lightning_sdk.lightning_cloud.openapi.models.v1_get_cluster_accelerator_demand_response import V1GetClusterAcceleratorDemandResponse
|
|
571
581
|
from lightning_sdk.lightning_cloud.openapi.models.v1_get_cluster_credentials_response import V1GetClusterCredentialsResponse
|
|
572
582
|
from lightning_sdk.lightning_cloud.openapi.models.v1_get_cluster_health_response import V1GetClusterHealthResponse
|
|
@@ -609,6 +619,7 @@ from lightning_sdk.lightning_cloud.openapi.models.v1_git_credentials import V1Gi
|
|
|
609
619
|
from lightning_sdk.lightning_cloud.openapi.models.v1_google_cloud_direct_v1 import V1GoogleCloudDirectV1
|
|
610
620
|
from lightning_sdk.lightning_cloud.openapi.models.v1_google_cloud_direct_v1_status import V1GoogleCloudDirectV1Status
|
|
611
621
|
from lightning_sdk.lightning_cloud.openapi.models.v1_group_node_metrics import V1GroupNodeMetrics
|
|
622
|
+
from lightning_sdk.lightning_cloud.openapi.models.v1_group_pod_metrics import V1GroupPodMetrics
|
|
612
623
|
from lightning_sdk.lightning_cloud.openapi.models.v1_guest_login_request import V1GuestLoginRequest
|
|
613
624
|
from lightning_sdk.lightning_cloud.openapi.models.v1_guest_login_response import V1GuestLoginResponse
|
|
614
625
|
from lightning_sdk.lightning_cloud.openapi.models.v1_guest_user import V1GuestUser
|
|
@@ -618,7 +629,9 @@ from lightning_sdk.lightning_cloud.openapi.models.v1_health_check_http_get impor
|
|
|
618
629
|
from lightning_sdk.lightning_cloud.openapi.models.v1_ids_logger_metrics import V1IdsLoggerMetrics
|
|
619
630
|
from lightning_sdk.lightning_cloud.openapi.models.v1_image_spec import V1ImageSpec
|
|
620
631
|
from lightning_sdk.lightning_cloud.openapi.models.v1_image_state import V1ImageState
|
|
632
|
+
from lightning_sdk.lightning_cloud.openapi.models.v1_incident import V1Incident
|
|
621
633
|
from lightning_sdk.lightning_cloud.openapi.models.v1_incident_event import V1IncidentEvent
|
|
634
|
+
from lightning_sdk.lightning_cloud.openapi.models.v1_incident_message import V1IncidentMessage
|
|
622
635
|
from lightning_sdk.lightning_cloud.openapi.models.v1_incident_severity import V1IncidentSeverity
|
|
623
636
|
from lightning_sdk.lightning_cloud.openapi.models.v1_incident_type import V1IncidentType
|
|
624
637
|
from lightning_sdk.lightning_cloud.openapi.models.v1_index import V1Index
|
|
@@ -641,6 +654,7 @@ from lightning_sdk.lightning_cloud.openapi.models.v1_job_timing import V1JobTimi
|
|
|
641
654
|
from lightning_sdk.lightning_cloud.openapi.models.v1_job_type import V1JobType
|
|
642
655
|
from lightning_sdk.lightning_cloud.openapi.models.v1_joinable_organization import V1JoinableOrganization
|
|
643
656
|
from lightning_sdk.lightning_cloud.openapi.models.v1_k8s_incident_indexes import V1K8sIncidentIndexes
|
|
657
|
+
from lightning_sdk.lightning_cloud.openapi.models.v1_kai_scheduler_queue_metrics import V1KaiSchedulerQueueMetrics
|
|
644
658
|
from lightning_sdk.lightning_cloud.openapi.models.v1_keep_alive_cloud_space_instance_response import V1KeepAliveCloudSpaceInstanceResponse
|
|
645
659
|
from lightning_sdk.lightning_cloud.openapi.models.v1_knowledge_configuration import V1KnowledgeConfiguration
|
|
646
660
|
from lightning_sdk.lightning_cloud.openapi.models.v1_kubernetes_direct_v1 import V1KubernetesDirectV1
|
|
@@ -721,11 +735,15 @@ from lightning_sdk.lightning_cloud.openapi.models.v1_list_filesystem_snowflake_r
|
|
|
721
735
|
from lightning_sdk.lightning_cloud.openapi.models.v1_list_gallery_components_response import V1ListGalleryComponentsResponse
|
|
722
736
|
from lightning_sdk.lightning_cloud.openapi.models.v1_list_gallery_lightningapps_response import V1ListGalleryLightningappsResponse
|
|
723
737
|
from lightning_sdk.lightning_cloud.openapi.models.v1_list_git_credentials_response import V1ListGitCredentialsResponse
|
|
738
|
+
from lightning_sdk.lightning_cloud.openapi.models.v1_list_group_pod_metrics_response import V1ListGroupPodMetricsResponse
|
|
724
739
|
from lightning_sdk.lightning_cloud.openapi.models.v1_list_incident_events_response import V1ListIncidentEventsResponse
|
|
740
|
+
from lightning_sdk.lightning_cloud.openapi.models.v1_list_incident_messages_response import V1ListIncidentMessagesResponse
|
|
741
|
+
from lightning_sdk.lightning_cloud.openapi.models.v1_list_incidents_response import V1ListIncidentsResponse
|
|
725
742
|
from lightning_sdk.lightning_cloud.openapi.models.v1_list_job_files_response import V1ListJobFilesResponse
|
|
726
743
|
from lightning_sdk.lightning_cloud.openapi.models.v1_list_job_resources_response import V1ListJobResourcesResponse
|
|
727
744
|
from lightning_sdk.lightning_cloud.openapi.models.v1_list_jobs_response import V1ListJobsResponse
|
|
728
745
|
from lightning_sdk.lightning_cloud.openapi.models.v1_list_joinable_organizations_response import V1ListJoinableOrganizationsResponse
|
|
746
|
+
from lightning_sdk.lightning_cloud.openapi.models.v1_list_kai_scheduler_queues_metrics_response import V1ListKaiSchedulerQueuesMetricsResponse
|
|
729
747
|
from lightning_sdk.lightning_cloud.openapi.models.v1_list_kubernetes_templates_response import V1ListKubernetesTemplatesResponse
|
|
730
748
|
from lightning_sdk.lightning_cloud.openapi.models.v1_list_lightning_run_response import V1ListLightningRunResponse
|
|
731
749
|
from lightning_sdk.lightning_cloud.openapi.models.v1_list_lightningapp_instance_artifacts_response import V1ListLightningappInstanceArtifactsResponse
|
|
@@ -938,6 +956,8 @@ from lightning_sdk.lightning_cloud.openapi.models.v1_rule_condition import V1Rul
|
|
|
938
956
|
from lightning_sdk.lightning_cloud.openapi.models.v1_rule_effect import V1RuleEffect
|
|
939
957
|
from lightning_sdk.lightning_cloud.openapi.models.v1_rule_resource import V1RuleResource
|
|
940
958
|
from lightning_sdk.lightning_cloud.openapi.models.v1_s3_folder_data_connection import V1S3FolderDataConnection
|
|
959
|
+
from lightning_sdk.lightning_cloud.openapi.models.v1_sdk_command_history_severity import V1SDKCommandHistorySeverity
|
|
960
|
+
from lightning_sdk.lightning_cloud.openapi.models.v1_sdk_command_history_type import V1SDKCommandHistoryType
|
|
941
961
|
from lightning_sdk.lightning_cloud.openapi.models.v1_slurm_job import V1SLURMJob
|
|
942
962
|
from lightning_sdk.lightning_cloud.openapi.models.v1_ssh_key_pair import V1SSHKeyPair
|
|
943
963
|
from lightning_sdk.lightning_cloud.openapi.models.v1_ssh_public_key import V1SSHPublicKey
|
|
@@ -37,6 +37,7 @@ from lightning_sdk.lightning_cloud.openapi.api.product_license_service_api impor
|
|
|
37
37
|
from lightning_sdk.lightning_cloud.openapi.api.profiler_service_api import ProfilerServiceApi
|
|
38
38
|
from lightning_sdk.lightning_cloud.openapi.api.projects_service_api import ProjectsServiceApi
|
|
39
39
|
from lightning_sdk.lightning_cloud.openapi.api.quest_service_api import QuestServiceApi
|
|
40
|
+
from lightning_sdk.lightning_cloud.openapi.api.sdk_command_history_service_api import SDKCommandHistoryServiceApi
|
|
40
41
|
from lightning_sdk.lightning_cloud.openapi.api.ssh_public_key_service_api import SSHPublicKeyServiceApi
|
|
41
42
|
from lightning_sdk.lightning_cloud.openapi.api.schedules_service_api import SchedulesServiceApi
|
|
42
43
|
from lightning_sdk.lightning_cloud.openapi.api.secret_service_api import SecretServiceApi
|