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.
Files changed (115) hide show
  1. anyscale/__init__.py +46 -0
  2. anyscale/_private/anyscale_client/anyscale_client.py +148 -28
  3. anyscale/_private/anyscale_client/common.py +74 -1
  4. anyscale/_private/anyscale_client/fake_anyscale_client.py +165 -1
  5. anyscale/_private/docgen/README.md +1 -1
  6. anyscale/_private/docgen/__main__.py +62 -19
  7. anyscale/_private/docgen/api.md +0 -20
  8. anyscale/_private/docgen/generator.py +3 -2
  9. anyscale/_private/docgen/models.md +1 -46
  10. anyscale/_private/workload/workload_config.py +1 -1
  11. anyscale/aggregated_instance_usage/__init__.py +1 -1
  12. anyscale/aggregated_instance_usage/commands.py +2 -4
  13. anyscale/aggregated_instance_usage/models.py +8 -8
  14. anyscale/client/README.md +15 -22
  15. anyscale/client/openapi_client/__init__.py +10 -14
  16. anyscale/client/openapi_client/api/default_api.py +634 -957
  17. anyscale/client/openapi_client/models/__init__.py +10 -14
  18. anyscale/client/openapi_client/models/{session_event_types.py → cloud_deployment_config.py} +35 -24
  19. anyscale/client/openapi_client/models/{platformfinetuningjob_response.py → clouddeploymentconfig_response.py} +11 -11
  20. anyscale/client/openapi_client/models/{company_size.py → cluster_size.py} +10 -10
  21. anyscale/client/openapi_client/models/cluster_status_details.py +2 -1
  22. anyscale/client/openapi_client/models/create_experimental_workspace.py +29 -1
  23. anyscale/client/openapi_client/models/{resubmit_ft_job_request.py → describe_machine_pool_request.py} +21 -20
  24. anyscale/client/openapi_client/models/describe_machine_pool_response.py +123 -0
  25. anyscale/client/openapi_client/models/{fine_tuning_job_status.py → describemachinepoolresponse_response.py} +34 -16
  26. anyscale/client/openapi_client/models/machine_allocation_state.py +3 -1
  27. anyscale/client/openapi_client/models/machine_state_info.py +326 -0
  28. anyscale/client/openapi_client/models/organization_marketing_questions.py +80 -54
  29. anyscale/client/openapi_client/models/request_state_info.py +210 -0
  30. anyscale/client/openapi_client/models/{sessionevent_list_response.py → scheduler_info.py} +43 -38
  31. anyscale/client/openapi_client/models/usage_by_cluster.py +28 -1
  32. anyscale/client/openapi_client/models/usage_by_user.py +30 -3
  33. anyscale/client/openapi_client/models/workload_info.py +210 -0
  34. anyscale/cloud/__init__.py +83 -0
  35. anyscale/cloud/_private/cloud_sdk.py +25 -0
  36. anyscale/cloud/commands.py +45 -0
  37. anyscale/cloud/models.py +91 -0
  38. anyscale/cluster_compute.py +1 -1
  39. anyscale/commands/aggregated_instance_usage_commands.py +4 -4
  40. anyscale/commands/cloud_commands.py +38 -2
  41. anyscale/commands/command_examples.py +61 -0
  42. anyscale/commands/job_commands.py +15 -3
  43. anyscale/commands/machine_pool_commands.py +113 -1
  44. anyscale/commands/organization_invitation_commands.py +98 -0
  45. anyscale/commands/project_commands.py +52 -2
  46. anyscale/commands/resource_quota_commands.py +98 -11
  47. anyscale/commands/service_commands.py +1 -1
  48. anyscale/commands/session_commands_hidden.py +5 -1
  49. anyscale/commands/user_commands.py +1 -1
  50. anyscale/commands/util.py +2 -2
  51. anyscale/commands/workspace_commands.py +1 -1
  52. anyscale/connect.py +1 -1
  53. anyscale/connect_utils/project.py +7 -4
  54. anyscale/controllers/cloud_controller.py +6 -6
  55. anyscale/controllers/cloud_functional_verification_controller.py +1 -1
  56. anyscale/controllers/cluster_controller.py +2 -2
  57. anyscale/controllers/compute_config_controller.py +1 -1
  58. anyscale/controllers/experimental_integrations_controller.py +1 -1
  59. anyscale/controllers/job_controller.py +8 -3
  60. anyscale/controllers/list_controller.py +2 -2
  61. anyscale/controllers/machine_pool_controller.py +12 -1
  62. anyscale/controllers/project_controller.py +4 -3
  63. anyscale/controllers/schedule_controller.py +1 -1
  64. anyscale/controllers/service_controller.py +1 -1
  65. anyscale/controllers/workspace_controller.py +1 -1
  66. anyscale/models/job_model.py +1 -1
  67. anyscale/organization_invitation/__init__.py +61 -0
  68. anyscale/organization_invitation/_private/organization_invitation_sdk.py +24 -0
  69. anyscale/organization_invitation/commands.py +84 -0
  70. anyscale/organization_invitation/models.py +45 -0
  71. anyscale/project/__init__.py +35 -0
  72. anyscale/project/_private/project_sdk.py +27 -0
  73. anyscale/project/commands.py +56 -0
  74. anyscale/project/models.py +91 -0
  75. anyscale/{project.py → project_utils.py} +3 -4
  76. anyscale/resource_quota/__init__.py +99 -0
  77. anyscale/resource_quota/_private/resource_quota_sdk.py +111 -0
  78. anyscale/resource_quota/commands.py +150 -0
  79. anyscale/resource_quota/models.py +303 -0
  80. anyscale/scripts.py +4 -0
  81. anyscale/sdk/anyscale_client/__init__.py +0 -5
  82. anyscale/sdk/anyscale_client/api/default_api.py +0 -150
  83. anyscale/sdk/anyscale_client/models/__init__.py +0 -5
  84. anyscale/sdk/anyscale_client/models/cluster_status_details.py +2 -1
  85. anyscale/sdk/anyscale_client/sdk.py +1 -1
  86. anyscale/user/__init__.py +1 -1
  87. anyscale/user/commands.py +1 -1
  88. anyscale/user/models.py +25 -15
  89. anyscale/util.py +15 -0
  90. anyscale/utils/cloud_utils.py +1 -1
  91. anyscale/version.py +1 -1
  92. anyscale/workspace_utils.py +1 -1
  93. {anyscale-0.24.88.dist-info → anyscale-0.25.0.dist-info}/METADATA +1 -5
  94. {anyscale-0.24.88.dist-info → anyscale-0.25.0.dist-info}/RECORD +100 -94
  95. anyscale/client/openapi_client/models/create_fine_tuning_hyperparameters.py +0 -156
  96. anyscale/client/openapi_client/models/create_fine_tuning_job_product_request.py +0 -353
  97. anyscale/client/openapi_client/models/finish_ft_job_request.py +0 -204
  98. anyscale/client/openapi_client/models/log_level_types.py +0 -100
  99. anyscale/client/openapi_client/models/platform_fine_tuning_job.py +0 -577
  100. anyscale/client/openapi_client/models/platformfinetuningjob_list_response.py +0 -147
  101. anyscale/client/openapi_client/models/session_event.py +0 -267
  102. anyscale/client/openapi_client/models/session_event_cause.py +0 -150
  103. anyscale/controllers/resource_quota_controller.py +0 -183
  104. anyscale/sdk/anyscale_client/models/log_level_types.py +0 -100
  105. anyscale/sdk/anyscale_client/models/session_event.py +0 -267
  106. anyscale/sdk/anyscale_client/models/session_event_cause.py +0 -150
  107. anyscale/sdk/anyscale_client/models/session_event_types.py +0 -111
  108. anyscale/sdk/anyscale_client/models/sessionevent_list_response.py +0 -147
  109. anyscale/utils/imports/azure.py +0 -14
  110. /anyscale/{cloud.py → cloud_utils.py} +0 -0
  111. {anyscale-0.24.88.dist-info → anyscale-0.25.0.dist-info}/LICENSE +0 -0
  112. {anyscale-0.24.88.dist-info → anyscale-0.25.0.dist-info}/NOTICE +0 -0
  113. {anyscale-0.24.88.dist-info → anyscale-0.25.0.dist-info}/WHEEL +0 -0
  114. {anyscale-0.24.88.dist-info → anyscale-0.25.0.dist-info}/entry_points.txt +0 -0
  115. {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(table, tablefmt="plain", headers=columns, stralign="left")
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.util import LegacyAnyscaleCommand, NotRequiredIf
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 validate_project_name
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.controllers.resource_quota_controller import ResourceQuotaController
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
- resource_quota_controller = ResourceQuotaController()
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
- resource_quota_controller = ResourceQuotaController()
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
- resource_quota_controller = ResourceQuotaController()
172
- resource_quota_controller.delete(id)
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
- resource_quota_controller = ResourceQuotaController()
193
- resource_quota_controller.set_status(id, is_enabled=True)
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
- resource_quota_controller = ResourceQuotaController()
214
- resource_quota_controller.set_status(id, is_enabled=False)
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. These will be installed on top of the image. When running in a workspace, this defaults to the workspace dependencies.",
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.project import get_project_id, get_project_session, load_project_or_throw
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
@@ -29,7 +29,7 @@ def user_cli() -> None:
29
29
  )
30
30
  def admin_batch_create(users_file: str,) -> None:
31
31
  """
32
- Batch create users without email verification as an admin.
32
+ Batch create, as an admin, users without email verification.
33
33
  """
34
34
  log.info("Creating users...")
35
35
 
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.project import find_project_root
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.project
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.cloud import (
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.project.find_project_root(os.getcwd())
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.project.ProjectDefinition(project_dir)
190
- project_id: Optional[str] = anyscale.project.get_project_id(proj_def.root)
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.project import get_default_project
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.cloud import get_cloud_id_and_name, get_last_used_cloud
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.project import (
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.cloud import get_cloud_id_and_name
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.cloud import get_cloud_id_and_name
6
+ from anyscale.cloud_utils import get_cloud_id_and_name
7
7
  from anyscale.controllers.base_controller import BaseController
8
8
 
9
9