lightning-sdk 0.1.58__py3-none-any.whl → 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lightning_sdk/__init__.py +5 -3
- lightning_sdk/api/deployment_api.py +23 -11
- lightning_sdk/api/job_api.py +42 -7
- lightning_sdk/api/lit_container_api.py +7 -3
- lightning_sdk/api/mmt_api.py +46 -8
- lightning_sdk/api/pipeline_api.py +50 -0
- lightning_sdk/api/teamspace_api.py +2 -2
- lightning_sdk/api/utils.py +15 -5
- lightning_sdk/cli/ai_hub.py +30 -65
- lightning_sdk/cli/coloring.py +60 -0
- lightning_sdk/cli/configure.py +25 -40
- lightning_sdk/cli/connect.py +7 -20
- lightning_sdk/cli/create.py +83 -0
- lightning_sdk/cli/delete.py +72 -75
- lightning_sdk/cli/docker.py +77 -0
- lightning_sdk/cli/download.py +71 -111
- lightning_sdk/cli/entrypoint.py +44 -65
- lightning_sdk/cli/generate.py +28 -43
- lightning_sdk/cli/inspect.py +22 -50
- lightning_sdk/cli/list.py +281 -222
- lightning_sdk/cli/mmts_menu.py +1 -1
- lightning_sdk/cli/open.py +62 -0
- lightning_sdk/cli/run.py +430 -263
- lightning_sdk/cli/serve.py +162 -189
- lightning_sdk/cli/start.py +55 -36
- lightning_sdk/cli/stop.py +97 -55
- lightning_sdk/cli/switch.py +53 -36
- lightning_sdk/cli/upload.py +318 -255
- lightning_sdk/deployment/__init__.py +2 -0
- lightning_sdk/deployment/deployment.py +33 -8
- lightning_sdk/lightning_cloud/openapi/__init__.py +21 -0
- lightning_sdk/lightning_cloud/openapi/api/__init__.py +1 -0
- lightning_sdk/lightning_cloud/openapi/api/assistants_service_api.py +10 -6
- lightning_sdk/lightning_cloud/openapi/api/jobs_service_api.py +355 -4
- lightning_sdk/lightning_cloud/openapi/api/lit_logger_service_api.py +4 -4
- lightning_sdk/lightning_cloud/openapi/api/lit_registry_service_api.py +14 -2
- lightning_sdk/lightning_cloud/openapi/api/pipelines_service_api.py +670 -0
- lightning_sdk/lightning_cloud/openapi/api/storage_service_api.py +303 -4
- lightning_sdk/lightning_cloud/openapi/models/__init__.py +20 -0
- lightning_sdk/lightning_cloud/openapi/models/agents_id_body.py +17 -69
- lightning_sdk/lightning_cloud/openapi/models/cluster_id_capacityreservations_body.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/create.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/create_deployment_request_defines_a_spec_for_the_job_that_allows_for_autoscaling_jobs.py +53 -1
- lightning_sdk/lightning_cloud/openapi/models/deployments_id_body.py +105 -1
- lightning_sdk/lightning_cloud/openapi/models/id_visibility_body1.py +1 -27
- lightning_sdk/lightning_cloud/openapi/models/id_visibility_body2.py +149 -0
- lightning_sdk/lightning_cloud/openapi/models/org_id_memberships_body.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/orgs_id_body.py +157 -1
- lightning_sdk/lightning_cloud/openapi/models/pipelines_id_body.py +435 -0
- lightning_sdk/lightning_cloud/openapi/models/project_id_pipelines_body.py +201 -0
- lightning_sdk/lightning_cloud/openapi/models/projects_id_body.py +157 -1
- lightning_sdk/lightning_cloud/openapi/models/slurm_jobs_body.py +79 -1
- lightning_sdk/lightning_cloud/openapi/models/uploads_upload_id_body.py +1 -27
- lightning_sdk/lightning_cloud/openapi/models/uploads_upload_id_body1.py +175 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_agent_job.py +79 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_assistant.py +17 -69
- lightning_sdk/lightning_cloud/openapi/models/v1_capacity_block_offering.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_cloud_space_artifact_event_type.py +1 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_cluster_accelerator.py +131 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_cluster_capacity_reservation.py +79 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_cluster_security_options.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_complete_upload_temporary_artifact_request.py +175 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_create_deployment_request.py +461 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_create_deployment_template_request.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_create_job_request.py +201 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_create_managed_endpoint_response.py +149 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_create_multi_machine_job_request.py +253 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_data_connection.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_delete_pipeline_response.py +149 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_deployment.py +105 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_deployment_details.py +175 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_deployment_template.py +53 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_filestore_data_connection.py +201 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_filesystem_job.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_filesystem_mmt.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_find_capacity_block_offering_response.py +29 -3
- lightning_sdk/lightning_cloud/openapi/models/v1_job.py +133 -3
- lightning_sdk/lightning_cloud/openapi/models/v1_job_spec.py +53 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_job_timing.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_list_pipelines_response.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_lit_registry_artifact.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_lit_repository.py +29 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_managed_model.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_multi_machine_job.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_multi_machine_job_state.py +2 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_organization.py +157 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_pipeline.py +487 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_pipeline_step.py +253 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_pipeline_step_status.py +331 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_pipeline_step_type.py +104 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_project_settings.py +157 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_restart_timing.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_rule_resource.py +1 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_shared_filesystem.py +201 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_slurm_job.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_update_job_visibility_response.py +97 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_upload_temporary_artifact_request.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_user_features.py +95 -355
- lightning_sdk/lightning_cloud/openapi/models/validate.py +27 -1
- lightning_sdk/lightning_cloud/rest_client.py +4 -2
- lightning_sdk/machine.py +25 -1
- lightning_sdk/models.py +18 -12
- lightning_sdk/pipeline/__init__.py +4 -0
- lightning_sdk/pipeline/pipeline.py +109 -0
- lightning_sdk/pipeline/types.py +268 -0
- lightning_sdk/pipeline/utils.py +69 -0
- lightning_sdk/plugin.py +9 -10
- lightning_sdk/services/utilities.py +2 -2
- lightning_sdk/studio.py +5 -1
- lightning_sdk/teamspace.py +1 -1
- lightning_sdk/utils/resolve.py +12 -1
- {lightning_sdk-0.1.58.dist-info → lightning_sdk-0.2.0.dist-info}/METADATA +6 -8
- {lightning_sdk-0.1.58.dist-info → lightning_sdk-0.2.0.dist-info}/RECORD +117 -88
- lightning_sdk/cli/legacy.py +0 -135
- {lightning_sdk-0.1.58.dist-info → lightning_sdk-0.2.0.dist-info}/LICENSE +0 -0
- {lightning_sdk-0.1.58.dist-info → lightning_sdk-0.2.0.dist-info}/WHEEL +0 -0
- {lightning_sdk-0.1.58.dist-info → lightning_sdk-0.2.0.dist-info}/entry_points.txt +0 -0
- {lightning_sdk-0.1.58.dist-info → lightning_sdk-0.2.0.dist-info}/top_level.txt +0 -0
lightning_sdk/cli/list.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from contextlib import suppress
|
|
2
2
|
from typing import Callable, Optional
|
|
3
3
|
|
|
4
|
+
import click
|
|
4
5
|
from rich.console import Console
|
|
5
6
|
from rich.table import Table
|
|
6
7
|
from typing_extensions import Literal
|
|
@@ -12,233 +13,291 @@ from lightning_sdk.lit_container import LitContainer
|
|
|
12
13
|
from lightning_sdk.utils.resolve import _get_authed_user
|
|
13
14
|
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
@click.group(name="list")
|
|
17
|
+
def list_cli() -> None:
|
|
16
18
|
"""List resources on the Lightning AI platform."""
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
20
|
+
|
|
21
|
+
@list_cli.command(name="studios")
|
|
22
|
+
@click.option(
|
|
23
|
+
"--teamspace",
|
|
24
|
+
default=None,
|
|
25
|
+
help=(
|
|
26
|
+
"the teamspace to list studios from. Should be specified as {owner}/{name}"
|
|
27
|
+
"If not provided, can be selected in an interactive menu."
|
|
28
|
+
),
|
|
29
|
+
)
|
|
30
|
+
@click.option(
|
|
31
|
+
"--all",
|
|
32
|
+
is_flag=True,
|
|
33
|
+
flag_value=True,
|
|
34
|
+
default=False,
|
|
35
|
+
help="if teamspace is not provided, list all studios in all teamspaces.",
|
|
36
|
+
)
|
|
37
|
+
@click.option(
|
|
38
|
+
"--sort-by",
|
|
39
|
+
default=None,
|
|
40
|
+
type=click.Choice(["name", "teamspace", "status", "machine", "cloud-account"], case_sensitive=False),
|
|
41
|
+
help="the attribute to sort the studios by.",
|
|
42
|
+
)
|
|
43
|
+
def studios(
|
|
44
|
+
teamspace: Optional[str] = None,
|
|
45
|
+
all: bool = False, # noqa: A002
|
|
46
|
+
sort_by: Optional[Literal["name", "teamspace", "status", "machine", "cloud-account"]] = None,
|
|
47
|
+
) -> None:
|
|
48
|
+
"""List studios for a given teamspace."""
|
|
49
|
+
studios = []
|
|
50
|
+
menu = _TeamspacesMenu()
|
|
51
|
+
if all and not teamspace:
|
|
52
|
+
user = _get_authed_user()
|
|
53
|
+
possible_teamspaces = menu._get_possible_teamspaces(user)
|
|
54
|
+
for ts in possible_teamspaces.values():
|
|
55
|
+
teamspace = Teamspace(**ts)
|
|
56
|
+
studios.extend(teamspace.studios)
|
|
57
|
+
else:
|
|
58
|
+
resolved_teamspace = menu._resolve_teamspace(teamspace=teamspace)
|
|
59
|
+
studios = resolved_teamspace.studios
|
|
60
|
+
|
|
61
|
+
table = Table(
|
|
62
|
+
pad_edge=True,
|
|
63
|
+
)
|
|
64
|
+
table.add_column("Name")
|
|
65
|
+
table.add_column("Teamspace")
|
|
66
|
+
table.add_column("Status")
|
|
67
|
+
table.add_column("Machine")
|
|
68
|
+
table.add_column("Cloud account")
|
|
69
|
+
for studio in sorted(studios, key=_sort_studios_key(sort_by)):
|
|
70
|
+
table.add_row(
|
|
71
|
+
studio.name,
|
|
72
|
+
f"{studio.teamspace.owner.name}/{studio.teamspace.name}",
|
|
73
|
+
str(studio.status),
|
|
74
|
+
str(studio.machine) if studio.machine is not None else None, # when None the cell is empty
|
|
75
|
+
str(studio.cloud_account),
|
|
59
76
|
)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
77
|
+
|
|
78
|
+
Console().print(table)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@list_cli.command(name="jobs")
|
|
82
|
+
@click.option(
|
|
83
|
+
"--teamspace",
|
|
84
|
+
default=None,
|
|
85
|
+
help=(
|
|
86
|
+
"the teamspace to list jobs from. Should be specified as {owner}/{name}"
|
|
87
|
+
"If not provided, can be selected in an interactive menu."
|
|
88
|
+
),
|
|
89
|
+
)
|
|
90
|
+
@click.option(
|
|
91
|
+
"--all",
|
|
92
|
+
is_flag=True,
|
|
93
|
+
flag_value=True,
|
|
94
|
+
default=False,
|
|
95
|
+
help="if teamspace is not provided, list all jobs in all teamspaces.",
|
|
96
|
+
)
|
|
97
|
+
@click.option(
|
|
98
|
+
"--sort-by",
|
|
99
|
+
"--sort_by",
|
|
100
|
+
default=None,
|
|
101
|
+
type=click.Choice(
|
|
102
|
+
["name", "teamspace", "status", "studio", "machine", "image", "cloud-account"], case_sensitive=False
|
|
103
|
+
),
|
|
104
|
+
help="the attribute to sort the jobs by.",
|
|
105
|
+
)
|
|
106
|
+
def jobs(
|
|
107
|
+
teamspace: Optional[str] = None,
|
|
108
|
+
all: bool = False, # noqa: A002
|
|
109
|
+
sort_by: Optional[Literal["name", "teamspace", "status", "studio", "machine", "image", "cloud-account"]] = None,
|
|
110
|
+
) -> None:
|
|
111
|
+
"""List jobs for a given teamspace."""
|
|
112
|
+
jobs = []
|
|
113
|
+
menu = _TeamspacesMenu()
|
|
114
|
+
if all and not teamspace:
|
|
115
|
+
user = _get_authed_user()
|
|
116
|
+
possible_teamspaces = menu._get_possible_teamspaces(user)
|
|
117
|
+
for ts in possible_teamspaces.values():
|
|
118
|
+
teamspace = Teamspace(**ts)
|
|
119
|
+
jobs.extend(teamspace.jobs)
|
|
120
|
+
else:
|
|
121
|
+
resolved_teamspace = menu._resolve_teamspace(teamspace=teamspace)
|
|
122
|
+
jobs = resolved_teamspace.jobs
|
|
123
|
+
|
|
124
|
+
table = Table(pad_edge=True)
|
|
125
|
+
table.add_column("Name")
|
|
126
|
+
table.add_column("Teamspace")
|
|
127
|
+
table.add_column("Studio")
|
|
128
|
+
table.add_column("Image")
|
|
129
|
+
table.add_column("Status")
|
|
130
|
+
table.add_column("Machine")
|
|
131
|
+
table.add_column("Total Cost")
|
|
132
|
+
for j in sorted(jobs, key=_sort_jobs_key(sort_by)):
|
|
133
|
+
# we know we just fetched these, so no need to refetch
|
|
134
|
+
j._prevent_refetch_latest = True
|
|
135
|
+
j._internal_job._prevent_refetch_latest = True
|
|
136
|
+
|
|
137
|
+
studio = j.studio
|
|
138
|
+
with suppress(RuntimeError):
|
|
66
139
|
table.add_row(
|
|
67
|
-
|
|
68
|
-
f"{
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
str(
|
|
140
|
+
j.name,
|
|
141
|
+
f"{j.teamspace.owner.name}/{j.teamspace.name}",
|
|
142
|
+
studio.name if studio else None,
|
|
143
|
+
j.image,
|
|
144
|
+
str(j.status) if j.status is not None else None,
|
|
145
|
+
str(j.machine),
|
|
146
|
+
f"{j.total_cost:.3f}",
|
|
72
147
|
)
|
|
73
148
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
jobs
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
149
|
+
Console().print(table)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
@list_cli.command(name="mmts")
|
|
153
|
+
@click.option(
|
|
154
|
+
"--teamspace",
|
|
155
|
+
default=None,
|
|
156
|
+
help=(
|
|
157
|
+
"the teamspace to list multi-machine jobs from. Should be specified as {owner}/{name}"
|
|
158
|
+
"If not provided, can be selected in an interactive menu."
|
|
159
|
+
),
|
|
160
|
+
)
|
|
161
|
+
@click.option(
|
|
162
|
+
"--all",
|
|
163
|
+
is_flag=True,
|
|
164
|
+
flag_value=True,
|
|
165
|
+
default=False,
|
|
166
|
+
help="if teamspace is not provided, list all multi-machine jobs in all teamspaces.",
|
|
167
|
+
)
|
|
168
|
+
@click.option(
|
|
169
|
+
"--sort-by",
|
|
170
|
+
"--sort_by",
|
|
171
|
+
default=None,
|
|
172
|
+
type=click.Choice(
|
|
173
|
+
["name", "teamspace", "studio", "image", "status", "machine", "cloud-account"], case_sensitive=False
|
|
174
|
+
),
|
|
175
|
+
help="the attribute to sort the multi-machine jobs by.",
|
|
176
|
+
)
|
|
177
|
+
def mmts(
|
|
178
|
+
teamspace: Optional[str] = None,
|
|
179
|
+
all: bool = False, # noqa: A002
|
|
180
|
+
sort_by: Optional[Literal["name", "teamspace", "studio", "image", "status", "machine", "cloud-account"]] = None,
|
|
181
|
+
) -> None:
|
|
182
|
+
"""List multi-machine jobs for a given teamspace."""
|
|
183
|
+
jobs = []
|
|
184
|
+
menu = _TeamspacesMenu()
|
|
185
|
+
if all and not teamspace:
|
|
186
|
+
user = _get_authed_user()
|
|
187
|
+
possible_teamspaces = menu._get_possible_teamspaces(user)
|
|
188
|
+
for ts in possible_teamspaces.values():
|
|
189
|
+
teamspace = Teamspace(**ts)
|
|
190
|
+
jobs.extend(teamspace.multi_machine_jobs)
|
|
191
|
+
else:
|
|
192
|
+
resolved_teamspace = menu._resolve_teamspace(teamspace=teamspace)
|
|
193
|
+
jobs = resolved_teamspace.multi_machine_jobs
|
|
194
|
+
|
|
195
|
+
table = Table(pad_edge=True)
|
|
196
|
+
table.add_column("Name")
|
|
197
|
+
table.add_column("Teamspace")
|
|
198
|
+
table.add_column("Studio")
|
|
199
|
+
table.add_column("Image")
|
|
200
|
+
table.add_column("Status")
|
|
201
|
+
table.add_column("Machine")
|
|
202
|
+
table.add_column("Num Machines")
|
|
203
|
+
table.add_column("Total Cost")
|
|
204
|
+
for j in sorted(jobs, key=_sort_mmts_key(sort_by)):
|
|
205
|
+
# we know we just fetched these, so no need to refetch
|
|
206
|
+
j._prevent_refetch_latest = True
|
|
207
|
+
with suppress(AttributeError):
|
|
128
208
|
j._internal_job._prevent_refetch_latest = True
|
|
129
209
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
)
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
)
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
""
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
"""
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
table = Table(pad_edge=True, box=None)
|
|
225
|
-
table.add_column("REPOSITORY")
|
|
226
|
-
table.add_column("IMAGE ID")
|
|
227
|
-
table.add_column("CREATED")
|
|
228
|
-
for repo in result:
|
|
229
|
-
table.add_row(repo["REPOSITORY"], repo["IMAGE ID"], repo["CREATED"])
|
|
230
|
-
Console().print(table)
|
|
231
|
-
|
|
232
|
-
def machines(self) -> None:
|
|
233
|
-
"""Display the list of available machines."""
|
|
234
|
-
table = Table(pad_edge=True)
|
|
235
|
-
table.add_column("Name")
|
|
236
|
-
|
|
237
|
-
# Get all machine types from the enum
|
|
238
|
-
machine_types = [name for name in dir(Machine) if not name.startswith("_")]
|
|
239
|
-
|
|
240
|
-
# Add rows to table
|
|
241
|
-
for name in sorted(machine_types):
|
|
242
|
-
table.add_row(name)
|
|
243
|
-
|
|
244
|
-
Console().print(table)
|
|
210
|
+
studio = j.studio
|
|
211
|
+
with suppress(RuntimeError):
|
|
212
|
+
table.add_row(
|
|
213
|
+
j.name,
|
|
214
|
+
f"{j.teamspace.owner.name}/{j.teamspace.name}",
|
|
215
|
+
studio.name if studio else None,
|
|
216
|
+
j.image,
|
|
217
|
+
str(j.status),
|
|
218
|
+
str(j.machine),
|
|
219
|
+
str(j.num_machines),
|
|
220
|
+
str(j.total_cost),
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
Console().print(table)
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
@list_cli.command(name="containers")
|
|
227
|
+
@click.option(
|
|
228
|
+
"--teamspace",
|
|
229
|
+
default=None,
|
|
230
|
+
help=(
|
|
231
|
+
"the teamspace to list containers from. Should be specified as {owner}/{name}"
|
|
232
|
+
"If not provided, can be selected in an interactive menu."
|
|
233
|
+
),
|
|
234
|
+
)
|
|
235
|
+
def containers(teamspace: Optional[str] = None) -> None:
|
|
236
|
+
"""Display the list of available containers."""
|
|
237
|
+
api = LitContainer()
|
|
238
|
+
menu = _TeamspacesMenu()
|
|
239
|
+
resolved_teamspace = menu._resolve_teamspace(teamspace=teamspace)
|
|
240
|
+
result = api.list_containers(teamspace=resolved_teamspace.name, org=resolved_teamspace.owner.name)
|
|
241
|
+
table = Table(pad_edge=True, box=None)
|
|
242
|
+
table.add_column("REPOSITORY")
|
|
243
|
+
table.add_column("IMAGE ID")
|
|
244
|
+
table.add_column("CREATED")
|
|
245
|
+
for repo in result:
|
|
246
|
+
table.add_row(repo["REPOSITORY"], repo["IMAGE ID"], repo["CREATED"])
|
|
247
|
+
Console().print(table)
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
@list_cli.command(name="machines")
|
|
251
|
+
def machines() -> None:
|
|
252
|
+
"""Display the list of available machines."""
|
|
253
|
+
table = Table(pad_edge=True)
|
|
254
|
+
table.add_column("Name")
|
|
255
|
+
|
|
256
|
+
# Get all machine types from the enum
|
|
257
|
+
machine_types = [name for name in dir(Machine) if isinstance(getattr(Machine, name), Machine)]
|
|
258
|
+
|
|
259
|
+
# Add rows to table
|
|
260
|
+
for name in sorted(machine_types):
|
|
261
|
+
table.add_row(name)
|
|
262
|
+
|
|
263
|
+
Console().print(table)
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def _sort_studios_key(sort_by: str) -> Callable[[Studio], str]:
|
|
267
|
+
"""Return a key function to sort studios by a given attribute."""
|
|
268
|
+
sort_key_map = {
|
|
269
|
+
"name": lambda s: str(s.name or ""),
|
|
270
|
+
"teamspace": lambda s: str(s.teamspace.name or ""),
|
|
271
|
+
"status": lambda s: str(s.status or ""),
|
|
272
|
+
"machine": lambda s: str(s.machine or ""),
|
|
273
|
+
"cloud-account": lambda s: str(s.cloud_account or ""),
|
|
274
|
+
}
|
|
275
|
+
return sort_key_map.get(sort_by, lambda s: s.name)
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def _sort_jobs_key(sort_by: str) -> Callable[[Job], str]:
|
|
279
|
+
"""Return a key function to sort studios by a given attribute."""
|
|
280
|
+
sort_key_map = {
|
|
281
|
+
"name": lambda j: str(j.name or ""),
|
|
282
|
+
"teamspace": lambda j: str(j.teamspace.name or ""),
|
|
283
|
+
"status": lambda j: str(j.status or ""),
|
|
284
|
+
"machine": lambda j: str(j.machine or ""),
|
|
285
|
+
"studio": lambda j: str(j.studio or ""),
|
|
286
|
+
"image": lambda j: str(j.image or ""),
|
|
287
|
+
"cloud-account": lambda j: str(j.cloud_account or ""),
|
|
288
|
+
}
|
|
289
|
+
return sort_key_map.get(sort_by, lambda j: j.name)
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def _sort_mmts_key(sort_by: str) -> Callable[[V1MultiMachineJob], str]:
|
|
293
|
+
"""Return a key function to sort multi-machine jobs by a given attribute."""
|
|
294
|
+
sort_key_map = {
|
|
295
|
+
"name": lambda j: str(j.name or ""),
|
|
296
|
+
"teamspace": lambda j: str(j.teamspace.name or ""),
|
|
297
|
+
"studio": lambda j: str(j.studio.name or ""),
|
|
298
|
+
"image": lambda j: str(j.image or ""),
|
|
299
|
+
"status": lambda j: str(j.status or ""),
|
|
300
|
+
"machine": lambda j: str(j.machine or ""),
|
|
301
|
+
"cloud-account": lambda j: str(j.cloud_account or ""),
|
|
302
|
+
}
|
|
303
|
+
return sort_key_map.get(sort_by, lambda j: j.name)
|
lightning_sdk/cli/mmts_menu.py
CHANGED
|
@@ -21,7 +21,7 @@ class _MMTsMenu:
|
|
|
21
21
|
if j.name == mmt:
|
|
22
22
|
return j
|
|
23
23
|
|
|
24
|
-
Console().print("Could not find Multi-Machine Job {mmt}, please select it from the list:")
|
|
24
|
+
Console().print(f"Could not find Multi-Machine Job {mmt}, please select it from the list:")
|
|
25
25
|
return self._get_mmt_from_interactive_menu(possible_mmts)
|
|
26
26
|
|
|
27
27
|
@staticmethod
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import webbrowser
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
import click
|
|
6
|
+
from rich.console import Console
|
|
7
|
+
|
|
8
|
+
from lightning_sdk.cli.teamspace_menu import _TeamspacesMenu
|
|
9
|
+
from lightning_sdk.cli.upload import _upload_folder
|
|
10
|
+
from lightning_sdk.studio import Studio
|
|
11
|
+
from lightning_sdk.teamspace import Teamspace
|
|
12
|
+
from lightning_sdk.utils.resolve import _get_studio_url
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@click.command("open")
|
|
16
|
+
@click.argument("path", default=".", type=click.Path(exists=True))
|
|
17
|
+
@click.option(
|
|
18
|
+
"--teamspace",
|
|
19
|
+
default=None,
|
|
20
|
+
help=(
|
|
21
|
+
"The teamspace to create the Studio in. "
|
|
22
|
+
"Should be of format <OWNER>/<TEAMSPACE_NAME>. "
|
|
23
|
+
"If not specified, tries to infer from the environment (e.g. when run from within a Studio.)"
|
|
24
|
+
),
|
|
25
|
+
)
|
|
26
|
+
def open(path: str = ".", teamspace: Optional[str] = None) -> None: # noqa: A001
|
|
27
|
+
"""Open a local file or folder in a Lightning Studio.
|
|
28
|
+
|
|
29
|
+
Example:
|
|
30
|
+
lightning open PATH
|
|
31
|
+
|
|
32
|
+
PATH: the path to the file or folder to open. Defaults to the current directory.
|
|
33
|
+
"""
|
|
34
|
+
console = Console()
|
|
35
|
+
|
|
36
|
+
pathlib_path = Path(path).resolve()
|
|
37
|
+
|
|
38
|
+
try:
|
|
39
|
+
resolved_teamspace = Teamspace()
|
|
40
|
+
except ValueError:
|
|
41
|
+
menu = _TeamspacesMenu()
|
|
42
|
+
resolved_teamspace = menu._resolve_teamspace(teamspace=teamspace)
|
|
43
|
+
|
|
44
|
+
new_studio = Studio(name=pathlib_path.stem, teamspace=resolved_teamspace)
|
|
45
|
+
|
|
46
|
+
console.print(
|
|
47
|
+
f"[bold]Uploading {path} to {new_studio.owner.name}/{new_studio.teamspace.name}/{new_studio.name}[/bold]"
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
if pathlib_path.is_dir():
|
|
51
|
+
_upload_folder(path, remote_path=".", studio=new_studio)
|
|
52
|
+
else:
|
|
53
|
+
new_studio.upload_file(path)
|
|
54
|
+
|
|
55
|
+
studio_url = _get_studio_url(new_studio, turn_on=True)
|
|
56
|
+
|
|
57
|
+
console.line()
|
|
58
|
+
console.print(f"[bold]Opening {new_studio.owner.name}/{new_studio.teamspace.name}/{new_studio.name}[/bold]")
|
|
59
|
+
|
|
60
|
+
ok = webbrowser.open(studio_url)
|
|
61
|
+
if not ok:
|
|
62
|
+
console.print(f"Open your Studio at: {studio_url}")
|