anyscale 0.24.88__py3-none-any.whl → 0.25.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.
- anyscale/__init__.py +46 -0
- anyscale/_private/anyscale_client/anyscale_client.py +148 -28
- anyscale/_private/anyscale_client/common.py +74 -1
- anyscale/_private/anyscale_client/fake_anyscale_client.py +165 -1
- anyscale/_private/docgen/README.md +1 -1
- anyscale/_private/docgen/__main__.py +62 -19
- anyscale/_private/docgen/api.md +0 -20
- anyscale/_private/docgen/generator.py +3 -2
- anyscale/_private/docgen/models.md +1 -46
- anyscale/_private/workload/workload_config.py +1 -1
- anyscale/aggregated_instance_usage/__init__.py +1 -1
- anyscale/aggregated_instance_usage/commands.py +2 -4
- anyscale/aggregated_instance_usage/models.py +8 -8
- anyscale/client/README.md +15 -22
- anyscale/client/openapi_client/__init__.py +10 -14
- anyscale/client/openapi_client/api/default_api.py +634 -957
- anyscale/client/openapi_client/models/__init__.py +10 -14
- anyscale/client/openapi_client/models/{session_event_types.py → cloud_deployment_config.py} +35 -24
- anyscale/client/openapi_client/models/{platformfinetuningjob_response.py → clouddeploymentconfig_response.py} +11 -11
- anyscale/client/openapi_client/models/{company_size.py → cluster_size.py} +10 -10
- anyscale/client/openapi_client/models/cluster_status_details.py +2 -1
- anyscale/client/openapi_client/models/create_experimental_workspace.py +29 -1
- anyscale/client/openapi_client/models/{resubmit_ft_job_request.py → describe_machine_pool_request.py} +21 -20
- anyscale/client/openapi_client/models/describe_machine_pool_response.py +123 -0
- anyscale/client/openapi_client/models/{fine_tuning_job_status.py → describemachinepoolresponse_response.py} +34 -16
- anyscale/client/openapi_client/models/machine_allocation_state.py +3 -1
- anyscale/client/openapi_client/models/machine_state_info.py +326 -0
- anyscale/client/openapi_client/models/organization_marketing_questions.py +80 -54
- anyscale/client/openapi_client/models/request_state_info.py +210 -0
- anyscale/client/openapi_client/models/{sessionevent_list_response.py → scheduler_info.py} +43 -38
- anyscale/client/openapi_client/models/usage_by_cluster.py +28 -1
- anyscale/client/openapi_client/models/usage_by_user.py +30 -3
- anyscale/client/openapi_client/models/workload_info.py +210 -0
- anyscale/cloud/__init__.py +83 -0
- anyscale/cloud/_private/cloud_sdk.py +25 -0
- anyscale/cloud/commands.py +45 -0
- anyscale/cloud/models.py +91 -0
- anyscale/cluster_compute.py +1 -1
- anyscale/commands/aggregated_instance_usage_commands.py +4 -4
- anyscale/commands/cloud_commands.py +38 -2
- anyscale/commands/command_examples.py +61 -0
- anyscale/commands/job_commands.py +15 -3
- anyscale/commands/machine_pool_commands.py +113 -1
- anyscale/commands/organization_invitation_commands.py +98 -0
- anyscale/commands/project_commands.py +52 -2
- anyscale/commands/resource_quota_commands.py +98 -11
- anyscale/commands/service_commands.py +1 -1
- anyscale/commands/session_commands_hidden.py +5 -1
- anyscale/commands/user_commands.py +1 -1
- anyscale/commands/util.py +2 -2
- anyscale/commands/workspace_commands.py +1 -1
- anyscale/connect.py +1 -1
- anyscale/connect_utils/project.py +7 -4
- anyscale/controllers/cloud_controller.py +6 -6
- anyscale/controllers/cloud_functional_verification_controller.py +1 -1
- anyscale/controllers/cluster_controller.py +2 -2
- anyscale/controllers/compute_config_controller.py +1 -1
- anyscale/controllers/experimental_integrations_controller.py +1 -1
- anyscale/controllers/job_controller.py +8 -3
- anyscale/controllers/list_controller.py +2 -2
- anyscale/controllers/machine_pool_controller.py +12 -1
- anyscale/controllers/project_controller.py +4 -3
- anyscale/controllers/schedule_controller.py +1 -1
- anyscale/controllers/service_controller.py +1 -1
- anyscale/controllers/workspace_controller.py +1 -1
- anyscale/models/job_model.py +1 -1
- anyscale/organization_invitation/__init__.py +61 -0
- anyscale/organization_invitation/_private/organization_invitation_sdk.py +24 -0
- anyscale/organization_invitation/commands.py +84 -0
- anyscale/organization_invitation/models.py +45 -0
- anyscale/project/__init__.py +35 -0
- anyscale/project/_private/project_sdk.py +27 -0
- anyscale/project/commands.py +56 -0
- anyscale/project/models.py +91 -0
- anyscale/{project.py → project_utils.py} +3 -4
- anyscale/resource_quota/__init__.py +99 -0
- anyscale/resource_quota/_private/resource_quota_sdk.py +111 -0
- anyscale/resource_quota/commands.py +150 -0
- anyscale/resource_quota/models.py +303 -0
- anyscale/scripts.py +4 -0
- anyscale/sdk/anyscale_client/__init__.py +0 -5
- anyscale/sdk/anyscale_client/api/default_api.py +0 -150
- anyscale/sdk/anyscale_client/models/__init__.py +0 -5
- anyscale/sdk/anyscale_client/models/cluster_status_details.py +2 -1
- anyscale/sdk/anyscale_client/sdk.py +1 -1
- anyscale/user/__init__.py +1 -1
- anyscale/user/commands.py +1 -1
- anyscale/user/models.py +25 -15
- anyscale/util.py +15 -0
- anyscale/utils/cloud_utils.py +1 -1
- anyscale/version.py +1 -1
- anyscale/workspace_utils.py +1 -1
- {anyscale-0.24.88.dist-info → anyscale-0.25.0.dist-info}/METADATA +1 -5
- {anyscale-0.24.88.dist-info → anyscale-0.25.0.dist-info}/RECORD +100 -94
- anyscale/client/openapi_client/models/create_fine_tuning_hyperparameters.py +0 -156
- anyscale/client/openapi_client/models/create_fine_tuning_job_product_request.py +0 -353
- anyscale/client/openapi_client/models/finish_ft_job_request.py +0 -204
- anyscale/client/openapi_client/models/log_level_types.py +0 -100
- anyscale/client/openapi_client/models/platform_fine_tuning_job.py +0 -577
- anyscale/client/openapi_client/models/platformfinetuningjob_list_response.py +0 -147
- anyscale/client/openapi_client/models/session_event.py +0 -267
- anyscale/client/openapi_client/models/session_event_cause.py +0 -150
- anyscale/controllers/resource_quota_controller.py +0 -183
- anyscale/sdk/anyscale_client/models/log_level_types.py +0 -100
- anyscale/sdk/anyscale_client/models/session_event.py +0 -267
- anyscale/sdk/anyscale_client/models/session_event_cause.py +0 -150
- anyscale/sdk/anyscale_client/models/session_event_types.py +0 -111
- anyscale/sdk/anyscale_client/models/sessionevent_list_response.py +0 -147
- anyscale/utils/imports/azure.py +0 -14
- /anyscale/{cloud.py → cloud_utils.py} +0 -0
- {anyscale-0.24.88.dist-info → anyscale-0.25.0.dist-info}/LICENSE +0 -0
- {anyscale-0.24.88.dist-info → anyscale-0.25.0.dist-info}/NOTICE +0 -0
- {anyscale-0.24.88.dist-info → anyscale-0.25.0.dist-info}/WHEEL +0 -0
- {anyscale-0.24.88.dist-info → anyscale-0.25.0.dist-info}/entry_points.txt +0 -0
- {anyscale-0.24.88.dist-info → anyscale-0.25.0.dist-info}/top_level.txt +0 -0
|
@@ -2,10 +2,16 @@
|
|
|
2
2
|
This file holds all of the CLI commands for the "anyscale machine-pool" path.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
import json
|
|
6
|
+
|
|
5
7
|
import click
|
|
6
8
|
import tabulate
|
|
7
9
|
import yaml
|
|
8
10
|
|
|
11
|
+
from anyscale.client.openapi_client.models import (
|
|
12
|
+
DescribeMachinePoolResponse,
|
|
13
|
+
SchedulerInfo,
|
|
14
|
+
)
|
|
9
15
|
from anyscale.commands import command_examples
|
|
10
16
|
from anyscale.commands.util import AnyscaleCommand
|
|
11
17
|
from anyscale.controllers.machine_pool_controller import MachinePoolController
|
|
@@ -71,6 +77,110 @@ def update_machine_pool(name: str, spec_file: str) -> None:
|
|
|
71
77
|
print(f"Updated machine pool '{name}'.")
|
|
72
78
|
|
|
73
79
|
|
|
80
|
+
@machine_pool_cli.command(
|
|
81
|
+
name="describe",
|
|
82
|
+
help="Describe a machine pool in Anyscale.",
|
|
83
|
+
cls=AnyscaleCommand,
|
|
84
|
+
example=command_examples.MACHINE_POOL_DESCRIBE_EXAMPLE,
|
|
85
|
+
is_beta=True,
|
|
86
|
+
)
|
|
87
|
+
@click.option("--name", type=str, required=True, help="Provide a machine pool name.")
|
|
88
|
+
@click.option(
|
|
89
|
+
"--format",
|
|
90
|
+
"format_",
|
|
91
|
+
type=str,
|
|
92
|
+
default="table",
|
|
93
|
+
required=False,
|
|
94
|
+
help="Output format (table, json).",
|
|
95
|
+
)
|
|
96
|
+
def describe(name: str, format_: str) -> None:
|
|
97
|
+
machine_pool_controller = MachinePoolController()
|
|
98
|
+
response: DescribeMachinePoolResponse = machine_pool_controller.describe_machine_pool(
|
|
99
|
+
machine_pool_name=name
|
|
100
|
+
)
|
|
101
|
+
scheduler_info: SchedulerInfo = response.scheduler_info # type: ignore
|
|
102
|
+
if format_ == "json":
|
|
103
|
+
print(json.dumps(scheduler_info.to_dict(), default=str))
|
|
104
|
+
elif format_ == "table":
|
|
105
|
+
machines_table = []
|
|
106
|
+
columns = [
|
|
107
|
+
"MACHINE ID",
|
|
108
|
+
"TYPE",
|
|
109
|
+
"PARTITION",
|
|
110
|
+
"STATE",
|
|
111
|
+
"WORKLOAD DETAILS",
|
|
112
|
+
"WORKLOAD SCORE",
|
|
113
|
+
"WORKLOAD START TIME",
|
|
114
|
+
"CLOUD INSTANCE ID",
|
|
115
|
+
]
|
|
116
|
+
for row in scheduler_info.machines:
|
|
117
|
+
machines_table.append(
|
|
118
|
+
[
|
|
119
|
+
row.machine_id,
|
|
120
|
+
row.machine_type,
|
|
121
|
+
row.partition,
|
|
122
|
+
row.allocation_state,
|
|
123
|
+
f"{row.workload_info.workload_type}/{row.workload_info.workload_name}/{row.workload_info.workload_cloud}"
|
|
124
|
+
if row.workload_info.workload_name
|
|
125
|
+
else "",
|
|
126
|
+
row.workload_score,
|
|
127
|
+
str(
|
|
128
|
+
row.workload_info.workload_start_time.astimezone().strftime(
|
|
129
|
+
"%m/%d/%Y %I:%M:%S %p %Z"
|
|
130
|
+
)
|
|
131
|
+
)
|
|
132
|
+
if row.workload_info.workload_name
|
|
133
|
+
else "",
|
|
134
|
+
row.cloud_instance_id,
|
|
135
|
+
]
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
# Sort by (type, partition, state, workload start time)
|
|
139
|
+
machines_table.sort(key=lambda x: (x[1], x[2], x[3], x[6]))
|
|
140
|
+
|
|
141
|
+
print("Machines:")
|
|
142
|
+
print(
|
|
143
|
+
tabulate.tabulate(
|
|
144
|
+
machines_table, tablefmt="outline", headers=columns, stralign="left"
|
|
145
|
+
)
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
requests_table = []
|
|
149
|
+
columns = [
|
|
150
|
+
"SIZE",
|
|
151
|
+
"MACHINE TYPE",
|
|
152
|
+
"WORKLOAD DETAILS",
|
|
153
|
+
"WORKLOAD START TIME",
|
|
154
|
+
"WORKLOAD CLOUD",
|
|
155
|
+
"PARTITION SCORES",
|
|
156
|
+
]
|
|
157
|
+
for row in scheduler_info.requests:
|
|
158
|
+
requests_table.append(
|
|
159
|
+
[
|
|
160
|
+
row.size,
|
|
161
|
+
row.machine_type,
|
|
162
|
+
f"{row.workload_info.workload_type}/{row.workload_info.workload_name}",
|
|
163
|
+
str(
|
|
164
|
+
row.workload_info.workload_start_time.astimezone().strftime(
|
|
165
|
+
"%m/%d/%Y %I:%M:%S %p %Z"
|
|
166
|
+
)
|
|
167
|
+
)
|
|
168
|
+
if row.workload_info.workload_name
|
|
169
|
+
else "",
|
|
170
|
+
row.workload_info.workload_cloud,
|
|
171
|
+
row.partition_scores,
|
|
172
|
+
]
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
# Sort by (machine type, workload start time, size)
|
|
176
|
+
print("Requests:")
|
|
177
|
+
print(
|
|
178
|
+
tabulate.tabulate(
|
|
179
|
+
requests_table, tablefmt="outline", headers=columns, stralign="left"
|
|
180
|
+
)
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
|
|
74
184
|
@machine_pool_cli.command(
|
|
75
185
|
name="delete",
|
|
76
186
|
help="Delete a machine pool in Anyscale.",
|
|
@@ -122,7 +232,9 @@ def list_machine_pools(format_: str) -> None:
|
|
|
122
232
|
]
|
|
123
233
|
)
|
|
124
234
|
print(
|
|
125
|
-
tabulate.tabulate(
|
|
235
|
+
tabulate.tabulate(
|
|
236
|
+
table, tablefmt="simple_grid", headers=columns, stralign="left"
|
|
237
|
+
)
|
|
126
238
|
)
|
|
127
239
|
elif format_ == "yaml":
|
|
128
240
|
rows = []
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import click
|
|
2
|
+
from dateutil import tz
|
|
3
|
+
from rich import print as rprint
|
|
4
|
+
import tabulate
|
|
5
|
+
|
|
6
|
+
import anyscale
|
|
7
|
+
from anyscale.cli_logger import BlockLogger
|
|
8
|
+
from anyscale.commands import command_examples
|
|
9
|
+
from anyscale.commands.util import AnyscaleCommand
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
log = BlockLogger() # CLI Logger
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@click.group("organization-invitation", help="Manage organization invitations.")
|
|
16
|
+
def organization_invitation_cli() -> None:
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@organization_invitation_cli.command(
|
|
21
|
+
name="create",
|
|
22
|
+
cls=AnyscaleCommand,
|
|
23
|
+
example=command_examples.ORGANIZATION_INVITATION_CREATE_EXAMPLE,
|
|
24
|
+
)
|
|
25
|
+
@click.option(
|
|
26
|
+
"--emails",
|
|
27
|
+
required=True,
|
|
28
|
+
type=str,
|
|
29
|
+
help="The emails to send the organization invitations to. Delimited by commas.",
|
|
30
|
+
)
|
|
31
|
+
def create(emails: str,) -> None:
|
|
32
|
+
"""
|
|
33
|
+
Creates organization invitations for the provided emails.
|
|
34
|
+
"""
|
|
35
|
+
log.info("Creating organization invitations...")
|
|
36
|
+
|
|
37
|
+
success_emails, error_messages = anyscale.organization_invitation.create(
|
|
38
|
+
emails.split(",")
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
if success_emails:
|
|
42
|
+
log.info(f"Organization invitations sent to: {', '.join(success_emails)}")
|
|
43
|
+
|
|
44
|
+
if error_messages:
|
|
45
|
+
for error_message in error_messages:
|
|
46
|
+
log.error(
|
|
47
|
+
f"Failed to send organization invitations with the following errors: {error_message}"
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@organization_invitation_cli.command(
|
|
52
|
+
name="list",
|
|
53
|
+
cls=AnyscaleCommand,
|
|
54
|
+
example=command_examples.ORGANIZATION_INVITATION_LIST_EXAMPLE,
|
|
55
|
+
)
|
|
56
|
+
def list() -> None: # noqa: A001
|
|
57
|
+
"""
|
|
58
|
+
Lists organization invitations.
|
|
59
|
+
"""
|
|
60
|
+
organization_invitations = anyscale.organization_invitation.list()
|
|
61
|
+
|
|
62
|
+
table = tabulate.tabulate(
|
|
63
|
+
[
|
|
64
|
+
(
|
|
65
|
+
i.id,
|
|
66
|
+
i.email,
|
|
67
|
+
i.created_at.astimezone(tz=tz.tzlocal()).strftime("%m/%d/%Y %I:%M %p"),
|
|
68
|
+
i.expires_at.astimezone(tz=tz.tzlocal()).strftime("%m/%d/%Y %I:%M %p"),
|
|
69
|
+
)
|
|
70
|
+
for i in organization_invitations
|
|
71
|
+
],
|
|
72
|
+
headers=["ID", "Email", "Created At", "Expires At"],
|
|
73
|
+
)
|
|
74
|
+
rprint(table)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@organization_invitation_cli.command(
|
|
78
|
+
name="delete",
|
|
79
|
+
cls=AnyscaleCommand,
|
|
80
|
+
example=command_examples.ORGANIZATION_INVITATION_DELETE_EXAMPLE,
|
|
81
|
+
)
|
|
82
|
+
@click.option(
|
|
83
|
+
"--email",
|
|
84
|
+
required=True,
|
|
85
|
+
type=str,
|
|
86
|
+
help="The email of the organization invitation to delete.",
|
|
87
|
+
)
|
|
88
|
+
def delete(email: str,) -> None:
|
|
89
|
+
"""
|
|
90
|
+
Deletes an organization invitation.
|
|
91
|
+
"""
|
|
92
|
+
try:
|
|
93
|
+
organization_invitation_email = anyscale.organization_invitation.delete(email)
|
|
94
|
+
except ValueError as e:
|
|
95
|
+
log.error(f"Failed to delete organization invitation: {e}")
|
|
96
|
+
return
|
|
97
|
+
|
|
98
|
+
log.info(f"Organization invitation for {organization_invitation_email} deleted.")
|
|
@@ -2,10 +2,16 @@ from typing import Optional
|
|
|
2
2
|
|
|
3
3
|
import click
|
|
4
4
|
|
|
5
|
+
import anyscale
|
|
5
6
|
from anyscale.cli_logger import BlockLogger
|
|
6
|
-
from anyscale.commands
|
|
7
|
+
from anyscale.commands import command_examples
|
|
8
|
+
from anyscale.commands.util import AnyscaleCommand, LegacyAnyscaleCommand, NotRequiredIf
|
|
7
9
|
from anyscale.controllers.project_controller import ProjectController
|
|
8
|
-
from anyscale.project import
|
|
10
|
+
from anyscale.project.models import (
|
|
11
|
+
CreateProjectCollaborator,
|
|
12
|
+
CreateProjectCollaborators,
|
|
13
|
+
)
|
|
14
|
+
from anyscale.project_utils import validate_project_name
|
|
9
15
|
from anyscale.util import validate_non_negative_arg
|
|
10
16
|
|
|
11
17
|
|
|
@@ -201,3 +207,47 @@ def init(project_id: Optional[str], name: Optional[str],) -> None:
|
|
|
201
207
|
def create(name: str, parent_cloud_id: str) -> None:
|
|
202
208
|
project_controller = ProjectController()
|
|
203
209
|
project_controller.create(name, parent_cloud_id)
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
@project_cli.command(
|
|
213
|
+
name="add-collaborators",
|
|
214
|
+
help="Add collaborators to the project.",
|
|
215
|
+
cls=AnyscaleCommand,
|
|
216
|
+
example=command_examples.PROJECT_ADD_COLLABORATORS_EXAMPLE,
|
|
217
|
+
)
|
|
218
|
+
@click.option(
|
|
219
|
+
"--cloud",
|
|
220
|
+
"-c",
|
|
221
|
+
help="Name of the cloud that the project belongs to.",
|
|
222
|
+
required=True,
|
|
223
|
+
)
|
|
224
|
+
@click.option(
|
|
225
|
+
"--project",
|
|
226
|
+
"-p",
|
|
227
|
+
help="Name of the project to add collaborators to.",
|
|
228
|
+
required=True,
|
|
229
|
+
)
|
|
230
|
+
@click.option(
|
|
231
|
+
"--users-file",
|
|
232
|
+
help="Path to a YAML file containing a list of users to add to the project.",
|
|
233
|
+
required=True,
|
|
234
|
+
)
|
|
235
|
+
def add_collaborators(cloud: str, project: str, users_file: str,) -> None:
|
|
236
|
+
collaborators = CreateProjectCollaborators.from_yaml(users_file)
|
|
237
|
+
|
|
238
|
+
try:
|
|
239
|
+
anyscale.project.add_collaborators(
|
|
240
|
+
cloud=cloud,
|
|
241
|
+
project=project,
|
|
242
|
+
collaborators=[
|
|
243
|
+
CreateProjectCollaborator(**collaborator)
|
|
244
|
+
for collaborator in collaborators.collaborators
|
|
245
|
+
],
|
|
246
|
+
)
|
|
247
|
+
except ValueError as e:
|
|
248
|
+
log.error(f"Error adding collaborators to project: {e}")
|
|
249
|
+
return
|
|
250
|
+
|
|
251
|
+
log.info(
|
|
252
|
+
f"Successfully added {len(collaborators.collaborators)} collaborators to project {project}."
|
|
253
|
+
)
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
from typing import List, Optional, Tuple
|
|
2
2
|
|
|
3
3
|
import click
|
|
4
|
+
from rich import print as rprint
|
|
5
|
+
import tabulate
|
|
4
6
|
|
|
7
|
+
import anyscale
|
|
5
8
|
from anyscale.cli_logger import BlockLogger
|
|
6
9
|
from anyscale.commands import command_examples
|
|
7
10
|
from anyscale.commands.util import AnyscaleCommand
|
|
8
|
-
from anyscale.
|
|
11
|
+
from anyscale.resource_quota.models import CreateResourceQuota, ResourceQuota
|
|
9
12
|
from anyscale.util import validate_non_negative_arg
|
|
10
13
|
|
|
11
14
|
|
|
@@ -17,6 +20,43 @@ def resource_quota_cli() -> None:
|
|
|
17
20
|
pass
|
|
18
21
|
|
|
19
22
|
|
|
23
|
+
def _format_resource_quotas(resource_quotas: List[ResourceQuota]) -> str:
|
|
24
|
+
table_rows = []
|
|
25
|
+
for resource_quota in resource_quotas:
|
|
26
|
+
table_rows.append(
|
|
27
|
+
[
|
|
28
|
+
resource_quota.id,
|
|
29
|
+
resource_quota.name,
|
|
30
|
+
resource_quota.cloud_id,
|
|
31
|
+
resource_quota.project_id,
|
|
32
|
+
resource_quota.user_id,
|
|
33
|
+
resource_quota.is_enabled,
|
|
34
|
+
resource_quota.created_at.strftime("%m/%d/%Y"),
|
|
35
|
+
resource_quota.deleted_at.strftime("%m/%d/%Y")
|
|
36
|
+
if resource_quota.deleted_at
|
|
37
|
+
else None,
|
|
38
|
+
resource_quota.quota,
|
|
39
|
+
]
|
|
40
|
+
)
|
|
41
|
+
table = tabulate.tabulate(
|
|
42
|
+
table_rows,
|
|
43
|
+
headers=[
|
|
44
|
+
"ID",
|
|
45
|
+
"NAME",
|
|
46
|
+
"CLOUD ID",
|
|
47
|
+
"PROJECT ID",
|
|
48
|
+
"USER ID",
|
|
49
|
+
"IS ENABLED",
|
|
50
|
+
"CREATED AT",
|
|
51
|
+
"DELETED AT",
|
|
52
|
+
"QUOTA",
|
|
53
|
+
],
|
|
54
|
+
tablefmt="plain",
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
return f"Resource quotas:\n{table}"
|
|
58
|
+
|
|
59
|
+
|
|
20
60
|
@resource_quota_cli.command(
|
|
21
61
|
name="create",
|
|
22
62
|
help="Create a resource quota.",
|
|
@@ -84,8 +124,7 @@ def create( # noqa: PLR0913
|
|
|
84
124
|
|
|
85
125
|
`$ anyscale resource-quota create -n my-resource-quota --cloud my-cloud --project my-project --user-email test@myorg.com --num-cpus 10 --num-instances 10 --num-gpus 10 --num-accelerators L4 5 --num-accelerators T4 10`
|
|
86
126
|
"""
|
|
87
|
-
|
|
88
|
-
resource_quota_controller.create(
|
|
127
|
+
create_resource_quota = CreateResourceQuota(
|
|
89
128
|
name=name,
|
|
90
129
|
cloud=cloud,
|
|
91
130
|
project=project,
|
|
@@ -96,6 +135,35 @@ def create( # noqa: PLR0913
|
|
|
96
135
|
num_accelerators=dict(num_accelerators),
|
|
97
136
|
)
|
|
98
137
|
|
|
138
|
+
try:
|
|
139
|
+
with log.spinner("Creating resource quota..."):
|
|
140
|
+
resource_quota = anyscale.resource_quota.create(create_resource_quota)
|
|
141
|
+
|
|
142
|
+
create_resource_quota_message = [f"Name: {name}\nCloud name: {cloud}"]
|
|
143
|
+
if project:
|
|
144
|
+
create_resource_quota_message.append(f"Project name: {project}")
|
|
145
|
+
if user_email:
|
|
146
|
+
create_resource_quota_message.append(f"User email: {user_email}")
|
|
147
|
+
if num_cpus:
|
|
148
|
+
create_resource_quota_message.append(f"Number of CPUs: {num_cpus}")
|
|
149
|
+
if num_instances:
|
|
150
|
+
create_resource_quota_message.append(
|
|
151
|
+
f"Number of instances: {num_instances}"
|
|
152
|
+
)
|
|
153
|
+
if num_gpus:
|
|
154
|
+
create_resource_quota_message.append(f"Number of GPUs: {num_gpus}")
|
|
155
|
+
if num_accelerators:
|
|
156
|
+
create_resource_quota_message.append(
|
|
157
|
+
f"Number of accelerators: {dict(num_accelerators)}"
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
log.info("\n".join(create_resource_quota_message))
|
|
161
|
+
log.info(f"Resource quota created successfully ID: {resource_quota.id}")
|
|
162
|
+
|
|
163
|
+
except ValueError as e:
|
|
164
|
+
log.error(f"Error creating resource quota: {e}")
|
|
165
|
+
return
|
|
166
|
+
|
|
99
167
|
|
|
100
168
|
@resource_quota_cli.command(
|
|
101
169
|
name="list",
|
|
@@ -141,8 +209,7 @@ def list_resource_quotas(
|
|
|
141
209
|
|
|
142
210
|
`$ anyscale resource-quota list -n my-resource-quota --cloud my-cloud`
|
|
143
211
|
"""
|
|
144
|
-
|
|
145
|
-
resource_quota_controller.list_resource_quotas(
|
|
212
|
+
resource_quotas = anyscale.resource_quota.list(
|
|
146
213
|
name=name,
|
|
147
214
|
cloud=cloud,
|
|
148
215
|
creator_id=creator_id,
|
|
@@ -150,6 +217,8 @@ def list_resource_quotas(
|
|
|
150
217
|
max_items=max_items,
|
|
151
218
|
)
|
|
152
219
|
|
|
220
|
+
rprint(_format_resource_quotas(resource_quotas))
|
|
221
|
+
|
|
153
222
|
|
|
154
223
|
@resource_quota_cli.command(
|
|
155
224
|
name="delete",
|
|
@@ -168,8 +237,14 @@ def delete(id: str) -> None: # noqa: A002
|
|
|
168
237
|
|
|
169
238
|
`$ anyscale resource-quota delete --id rsq_123`
|
|
170
239
|
"""
|
|
171
|
-
|
|
172
|
-
|
|
240
|
+
try:
|
|
241
|
+
with log.spinner("Deleting resource quota..."):
|
|
242
|
+
anyscale.resource_quota.delete(resource_quota_id=id)
|
|
243
|
+
except ValueError as e:
|
|
244
|
+
log.error(f"Error deleting resource quota: {e}")
|
|
245
|
+
return
|
|
246
|
+
|
|
247
|
+
log.info(f"Resource quota with ID {id} deleted successfully.")
|
|
173
248
|
|
|
174
249
|
|
|
175
250
|
@resource_quota_cli.command(
|
|
@@ -189,8 +264,14 @@ def enable(id: str) -> None: # noqa: A002
|
|
|
189
264
|
|
|
190
265
|
`$ anyscale resource-quota enable --id rsq_123`
|
|
191
266
|
"""
|
|
192
|
-
|
|
193
|
-
|
|
267
|
+
try:
|
|
268
|
+
with log.spinner("Setting resource quota status..."):
|
|
269
|
+
anyscale.resource_quota.enable(resource_quota_id=id)
|
|
270
|
+
except ValueError as e:
|
|
271
|
+
log.error(f"Error enabling resource quota: {e}")
|
|
272
|
+
return
|
|
273
|
+
|
|
274
|
+
log.info(f"Enabled resource quota with ID {id} successfully.")
|
|
194
275
|
|
|
195
276
|
|
|
196
277
|
@resource_quota_cli.command(
|
|
@@ -210,5 +291,11 @@ def disable(id: str) -> None: # noqa: A002
|
|
|
210
291
|
|
|
211
292
|
`$ anyscale resource-quota disable --id rsq_123`
|
|
212
293
|
"""
|
|
213
|
-
|
|
214
|
-
|
|
294
|
+
try:
|
|
295
|
+
with log.spinner("Setting resource quota status..."):
|
|
296
|
+
anyscale.resource_quota.disable(resource_quota_id=id)
|
|
297
|
+
except ValueError as e:
|
|
298
|
+
log.error(f"Error disabling resource quota: {e}")
|
|
299
|
+
return
|
|
300
|
+
|
|
301
|
+
log.info(f"Disabled resource quota with ID {id} successfully.")
|
|
@@ -125,7 +125,7 @@ def _read_name_from_config_file(path: str):
|
|
|
125
125
|
required=False,
|
|
126
126
|
default=None,
|
|
127
127
|
type=str,
|
|
128
|
-
help="Path to a requirements.txt file containing dependencies for the service.
|
|
128
|
+
help="Path to a requirements.txt file containing dependencies for the service. Anyscale installs these dependencies on top of the image. If you deploy a service from a workspace, the default is to use the workspace dependencies, but specifying this option overrides them.",
|
|
129
129
|
)
|
|
130
130
|
@click.option(
|
|
131
131
|
"-i",
|
|
@@ -4,7 +4,11 @@ from typing import Optional
|
|
|
4
4
|
|
|
5
5
|
import click
|
|
6
6
|
|
|
7
|
-
from anyscale.
|
|
7
|
+
from anyscale.project_utils import (
|
|
8
|
+
get_project_id,
|
|
9
|
+
get_project_session,
|
|
10
|
+
load_project_or_throw,
|
|
11
|
+
)
|
|
8
12
|
from anyscale.shared_anyscale_utils.util import execution_log_name
|
|
9
13
|
from anyscale.snapshot import copy_file
|
|
10
14
|
from anyscale.util import deserialize_datetime, send_json_request
|
anyscale/commands/util.py
CHANGED
|
@@ -125,8 +125,8 @@ def convert_kv_strings_to_dict(strings: Tuple[str]) -> Dict[str, str]:
|
|
|
125
125
|
"""
|
|
126
126
|
ret_dict = {}
|
|
127
127
|
for s in strings:
|
|
128
|
-
split = s.split("=")
|
|
129
|
-
if len(split) != 2:
|
|
128
|
+
split = s.split("=", maxsplit=1)
|
|
129
|
+
if len(split) != 2 or len(split[1]) == 0:
|
|
130
130
|
raise click.ClickException(
|
|
131
131
|
f"Invalid key-value string '{s}'. Must be of the form 'key=value'."
|
|
132
132
|
)
|
|
@@ -15,7 +15,7 @@ from anyscale.commands import workspace_commands_v2
|
|
|
15
15
|
from anyscale.commands.util import LegacyAnyscaleCommand
|
|
16
16
|
from anyscale.controllers.cluster_controller import ClusterController
|
|
17
17
|
from anyscale.controllers.workspace_controller import WorkspaceController
|
|
18
|
-
from anyscale.
|
|
18
|
+
from anyscale.project_utils import find_project_root
|
|
19
19
|
from anyscale.shared_anyscale_utils.utils.byod import BYODInfo
|
|
20
20
|
from anyscale.util import get_endpoint
|
|
21
21
|
from anyscale.workspace_utils import (
|
anyscale/connect.py
CHANGED
|
@@ -48,7 +48,7 @@ from anyscale.connect_utils.start_interactive_session import ( # pylint:disable
|
|
|
48
48
|
_get_interactive_shell_frame,
|
|
49
49
|
start_interactive_session_block,
|
|
50
50
|
)
|
|
51
|
-
import anyscale.
|
|
51
|
+
import anyscale.project_utils
|
|
52
52
|
from anyscale.sdk.anyscale_client.sdk import AnyscaleSDK
|
|
53
53
|
from anyscale.shared_anyscale_utils.util import slugify
|
|
54
54
|
from anyscale.util import PROJECT_NAME_ENV_VAR
|
|
@@ -7,13 +7,14 @@ import yaml
|
|
|
7
7
|
import anyscale
|
|
8
8
|
from anyscale.authenticate import get_auth_api_client
|
|
9
9
|
from anyscale.cli_logger import BlockLogger
|
|
10
|
-
from anyscale.
|
|
10
|
+
from anyscale.cloud_utils import (
|
|
11
11
|
get_cloud_id_and_name,
|
|
12
12
|
get_last_used_cloud,
|
|
13
13
|
get_organization_default_cloud,
|
|
14
14
|
)
|
|
15
15
|
from anyscale.cluster_compute import get_cluster_compute_from_name
|
|
16
16
|
import anyscale.conf
|
|
17
|
+
import anyscale.project_utils
|
|
17
18
|
from anyscale.sdk.anyscale_client import ComputeTemplateConfig
|
|
18
19
|
from anyscale.utils.connect_helpers import find_project_id
|
|
19
20
|
|
|
@@ -70,7 +71,7 @@ class ProjectBlock:
|
|
|
70
71
|
self.log.open_block(self.block_label, block_title="Choosing a project")
|
|
71
72
|
|
|
72
73
|
if self.project_dir is None and project_name is None:
|
|
73
|
-
self.project_dir = anyscale.
|
|
74
|
+
self.project_dir = anyscale.project_utils.find_project_root(os.getcwd())
|
|
74
75
|
|
|
75
76
|
parent_cloud_id = self._get_parent_cloud_id(
|
|
76
77
|
self.cloud_name, self.cluster_compute_name, self.cluster_compute_dict,
|
|
@@ -186,8 +187,10 @@ class ProjectBlock:
|
|
|
186
187
|
project_yaml = os.path.join(project_dir, ".anyscale.yaml")
|
|
187
188
|
if os.path.exists(project_yaml):
|
|
188
189
|
# Validate format of project yaml and get project id
|
|
189
|
-
proj_def = anyscale.
|
|
190
|
-
project_id: Optional[str] = anyscale.
|
|
190
|
+
proj_def = anyscale.project_utils.ProjectDefinition(project_dir)
|
|
191
|
+
project_id: Optional[str] = anyscale.project_utils.get_project_id(
|
|
192
|
+
proj_def.root
|
|
193
|
+
)
|
|
191
194
|
if not project_id:
|
|
192
195
|
raise click.ClickException(
|
|
193
196
|
f"{project_yaml} is not correctly formatted. Please attach to a different "
|
|
@@ -43,12 +43,6 @@ from anyscale.client.openapi_client.models import (
|
|
|
43
43
|
from anyscale.client.openapi_client.models.gcp_file_store_config import (
|
|
44
44
|
GCPFileStoreConfig,
|
|
45
45
|
)
|
|
46
|
-
from anyscale.cloud import (
|
|
47
|
-
get_cloud_id_and_name,
|
|
48
|
-
get_cloud_json_from_id,
|
|
49
|
-
get_cloud_resource_by_cloud_id,
|
|
50
|
-
get_organization_id,
|
|
51
|
-
)
|
|
52
46
|
from anyscale.cloud_resource import (
|
|
53
47
|
associate_aws_subnets_with_azs,
|
|
54
48
|
GCS_STORAGE_PREFIX,
|
|
@@ -63,6 +57,12 @@ from anyscale.cloud_resource import (
|
|
|
63
57
|
verify_aws_subnets,
|
|
64
58
|
verify_aws_vpc,
|
|
65
59
|
)
|
|
60
|
+
from anyscale.cloud_utils import (
|
|
61
|
+
get_cloud_id_and_name,
|
|
62
|
+
get_cloud_json_from_id,
|
|
63
|
+
get_cloud_resource_by_cloud_id,
|
|
64
|
+
get_organization_id,
|
|
65
|
+
)
|
|
66
66
|
from anyscale.conf import ANYSCALE_IAM_ROLE_NAME
|
|
67
67
|
from anyscale.controllers.base_controller import BaseController
|
|
68
68
|
from anyscale.controllers.cloud_functional_verification_controller import (
|
|
@@ -30,7 +30,7 @@ from anyscale.client.openapi_client.models import (
|
|
|
30
30
|
SessionState,
|
|
31
31
|
)
|
|
32
32
|
from anyscale.controllers.base_controller import BaseController
|
|
33
|
-
from anyscale.
|
|
33
|
+
from anyscale.project_utils import get_default_project
|
|
34
34
|
from anyscale.sdk.anyscale_client.models import (
|
|
35
35
|
ComputeNodeType,
|
|
36
36
|
ComputeTemplateQuery,
|
|
@@ -7,7 +7,7 @@ import tabulate
|
|
|
7
7
|
import yaml
|
|
8
8
|
|
|
9
9
|
from anyscale.cli_logger import BlockLogger
|
|
10
|
-
from anyscale.
|
|
10
|
+
from anyscale.cloud_utils import get_cloud_id_and_name, get_last_used_cloud
|
|
11
11
|
from anyscale.cluster_compute import (
|
|
12
12
|
get_cluster_compute_from_name,
|
|
13
13
|
get_default_cluster_compute,
|
|
@@ -18,7 +18,7 @@ from anyscale.cluster_env import (
|
|
|
18
18
|
get_default_cluster_env_build,
|
|
19
19
|
)
|
|
20
20
|
from anyscale.controllers.base_controller import BaseController
|
|
21
|
-
from anyscale.
|
|
21
|
+
from anyscale.project_utils import (
|
|
22
22
|
get_and_validate_project_id,
|
|
23
23
|
get_proj_id_from_name,
|
|
24
24
|
get_project_id,
|
|
@@ -14,7 +14,7 @@ from anyscale.anyscale_pydantic import (
|
|
|
14
14
|
validator,
|
|
15
15
|
)
|
|
16
16
|
from anyscale.cli_logger import BlockLogger
|
|
17
|
-
from anyscale.
|
|
17
|
+
from anyscale.cloud_utils import get_cloud_id_and_name
|
|
18
18
|
from anyscale.cluster_compute import get_cluster_compute_from_name
|
|
19
19
|
from anyscale.conf import IDLE_TIMEOUT_DEFAULT_MINUTES
|
|
20
20
|
from anyscale.controllers.base_controller import BaseController
|
|
@@ -3,7 +3,7 @@ from typing import Optional
|
|
|
3
3
|
import click
|
|
4
4
|
|
|
5
5
|
from anyscale.cli_logger import BlockLogger
|
|
6
|
-
from anyscale.
|
|
6
|
+
from anyscale.cloud_utils import get_cloud_id_and_name
|
|
7
7
|
from anyscale.controllers.base_controller import BaseController
|
|
8
8
|
|
|
9
9
|
|