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
@@ -0,0 +1,210 @@
1
+ # coding: utf-8
2
+
3
+ """
4
+ Managed Ray API
5
+
6
+ No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) # noqa: E501
7
+
8
+ The version of the OpenAPI document: 0.1.0
9
+ Generated by: https://openapi-generator.tech
10
+ """
11
+
12
+
13
+ import pprint
14
+ import re # noqa: F401
15
+
16
+ import six
17
+
18
+ from openapi_client.configuration import Configuration
19
+
20
+
21
+ class WorkloadInfo(object):
22
+ """NOTE: This class is auto generated by OpenAPI Generator.
23
+ Ref: https://openapi-generator.tech
24
+
25
+ Do not edit the class manually.
26
+ """
27
+
28
+ """
29
+ Attributes:
30
+ openapi_types (dict): The key is attribute name
31
+ and the value is attribute type.
32
+ attribute_map (dict): The key is attribute name
33
+ and the value is json key in definition.
34
+ """
35
+ openapi_types = {
36
+ 'workload_name': 'str',
37
+ 'workload_type': 'str',
38
+ 'workload_start_time': 'datetime',
39
+ 'workload_cloud': 'str'
40
+ }
41
+
42
+ attribute_map = {
43
+ 'workload_name': 'workload_name',
44
+ 'workload_type': 'workload_type',
45
+ 'workload_start_time': 'workload_start_time',
46
+ 'workload_cloud': 'workload_cloud'
47
+ }
48
+
49
+ def __init__(self, workload_name=None, workload_type=None, workload_start_time=None, workload_cloud=None, local_vars_configuration=None): # noqa: E501
50
+ """WorkloadInfo - a model defined in OpenAPI""" # noqa: E501
51
+ if local_vars_configuration is None:
52
+ local_vars_configuration = Configuration()
53
+ self.local_vars_configuration = local_vars_configuration
54
+
55
+ self._workload_name = None
56
+ self._workload_type = None
57
+ self._workload_start_time = None
58
+ self._workload_cloud = None
59
+ self.discriminator = None
60
+
61
+ self.workload_name = workload_name
62
+ self.workload_type = workload_type
63
+ self.workload_start_time = workload_start_time
64
+ self.workload_cloud = workload_cloud
65
+
66
+ @property
67
+ def workload_name(self):
68
+ """Gets the workload_name of this WorkloadInfo. # noqa: E501
69
+
70
+ The name of the workload. # noqa: E501
71
+
72
+ :return: The workload_name of this WorkloadInfo. # noqa: E501
73
+ :rtype: str
74
+ """
75
+ return self._workload_name
76
+
77
+ @workload_name.setter
78
+ def workload_name(self, workload_name):
79
+ """Sets the workload_name of this WorkloadInfo.
80
+
81
+ The name of the workload. # noqa: E501
82
+
83
+ :param workload_name: The workload_name of this WorkloadInfo. # noqa: E501
84
+ :type: str
85
+ """
86
+ if self.local_vars_configuration.client_side_validation and workload_name is None: # noqa: E501
87
+ raise ValueError("Invalid value for `workload_name`, must not be `None`") # noqa: E501
88
+
89
+ self._workload_name = workload_name
90
+
91
+ @property
92
+ def workload_type(self):
93
+ """Gets the workload_type of this WorkloadInfo. # noqa: E501
94
+
95
+ The type of the workload. # noqa: E501
96
+
97
+ :return: The workload_type of this WorkloadInfo. # noqa: E501
98
+ :rtype: str
99
+ """
100
+ return self._workload_type
101
+
102
+ @workload_type.setter
103
+ def workload_type(self, workload_type):
104
+ """Sets the workload_type of this WorkloadInfo.
105
+
106
+ The type of the workload. # noqa: E501
107
+
108
+ :param workload_type: The workload_type of this WorkloadInfo. # noqa: E501
109
+ :type: str
110
+ """
111
+ if self.local_vars_configuration.client_side_validation and workload_type is None: # noqa: E501
112
+ raise ValueError("Invalid value for `workload_type`, must not be `None`") # noqa: E501
113
+
114
+ self._workload_type = workload_type
115
+
116
+ @property
117
+ def workload_start_time(self):
118
+ """Gets the workload_start_time of this WorkloadInfo. # noqa: E501
119
+
120
+ The start time of the workload. # noqa: E501
121
+
122
+ :return: The workload_start_time of this WorkloadInfo. # noqa: E501
123
+ :rtype: datetime
124
+ """
125
+ return self._workload_start_time
126
+
127
+ @workload_start_time.setter
128
+ def workload_start_time(self, workload_start_time):
129
+ """Sets the workload_start_time of this WorkloadInfo.
130
+
131
+ The start time of the workload. # noqa: E501
132
+
133
+ :param workload_start_time: The workload_start_time of this WorkloadInfo. # noqa: E501
134
+ :type: datetime
135
+ """
136
+ if self.local_vars_configuration.client_side_validation and workload_start_time is None: # noqa: E501
137
+ raise ValueError("Invalid value for `workload_start_time`, must not be `None`") # noqa: E501
138
+
139
+ self._workload_start_time = workload_start_time
140
+
141
+ @property
142
+ def workload_cloud(self):
143
+ """Gets the workload_cloud of this WorkloadInfo. # noqa: E501
144
+
145
+ The cloud of the workload. # noqa: E501
146
+
147
+ :return: The workload_cloud of this WorkloadInfo. # noqa: E501
148
+ :rtype: str
149
+ """
150
+ return self._workload_cloud
151
+
152
+ @workload_cloud.setter
153
+ def workload_cloud(self, workload_cloud):
154
+ """Sets the workload_cloud of this WorkloadInfo.
155
+
156
+ The cloud of the workload. # noqa: E501
157
+
158
+ :param workload_cloud: The workload_cloud of this WorkloadInfo. # noqa: E501
159
+ :type: str
160
+ """
161
+ if self.local_vars_configuration.client_side_validation and workload_cloud is None: # noqa: E501
162
+ raise ValueError("Invalid value for `workload_cloud`, must not be `None`") # noqa: E501
163
+
164
+ self._workload_cloud = workload_cloud
165
+
166
+ def to_dict(self):
167
+ """Returns the model properties as a dict"""
168
+ result = {}
169
+
170
+ for attr, _ in six.iteritems(self.openapi_types):
171
+ value = getattr(self, attr)
172
+ if isinstance(value, list):
173
+ result[attr] = list(map(
174
+ lambda x: x.to_dict() if hasattr(x, "to_dict") else x,
175
+ value
176
+ ))
177
+ elif hasattr(value, "to_dict"):
178
+ result[attr] = value.to_dict()
179
+ elif isinstance(value, dict):
180
+ result[attr] = dict(map(
181
+ lambda item: (item[0], item[1].to_dict())
182
+ if hasattr(item[1], "to_dict") else item,
183
+ value.items()
184
+ ))
185
+ else:
186
+ result[attr] = value
187
+
188
+ return result
189
+
190
+ def to_str(self):
191
+ """Returns the string representation of the model"""
192
+ return pprint.pformat(self.to_dict())
193
+
194
+ def __repr__(self):
195
+ """For `print` and `pprint`"""
196
+ return self.to_str()
197
+
198
+ def __eq__(self, other):
199
+ """Returns true if both objects are equal"""
200
+ if not isinstance(other, WorkloadInfo):
201
+ return False
202
+
203
+ return self.to_dict() == other.to_dict()
204
+
205
+ def __ne__(self, other):
206
+ """Returns true if both objects are not equal"""
207
+ if not isinstance(other, WorkloadInfo):
208
+ return True
209
+
210
+ return self.to_dict() != other.to_dict()
@@ -0,0 +1,83 @@
1
+ import inspect
2
+ import sys
3
+ from types import ModuleType
4
+ from typing import Any, Dict, List, Optional
5
+
6
+ from anyscale._private.anyscale_client import AnyscaleClientInterface
7
+ from anyscale._private.sdk import sdk_docs
8
+ from anyscale._private.sdk.base_sdk import Timer
9
+ from anyscale.cli_logger import BlockLogger
10
+ from anyscale.cloud._private.cloud_sdk import PrivateCloudSDK
11
+ from anyscale.cloud.commands import (
12
+ _ADD_COLLABORATORS_ARG_DOCSTRINGS,
13
+ _ADD_COLLABORATORS_EXAMPLE,
14
+ add_collaborators,
15
+ )
16
+ from anyscale.cloud.models import CreateCloudCollaborator
17
+ from anyscale.connect import ClientBuilder
18
+
19
+
20
+ class CloudSDK:
21
+ def __init__(
22
+ self,
23
+ *,
24
+ client: Optional[AnyscaleClientInterface] = None,
25
+ logger: Optional[BlockLogger] = None,
26
+ timer: Optional[Timer] = None,
27
+ ):
28
+ self._private_sdk = PrivateCloudSDK(client=client, logger=logger, timer=timer)
29
+
30
+ @sdk_docs(
31
+ doc_py_example=_ADD_COLLABORATORS_EXAMPLE,
32
+ arg_docstrings=_ADD_COLLABORATORS_ARG_DOCSTRINGS,
33
+ )
34
+ def add_collaborators( # noqa: F811
35
+ self, cloud: str, collaborators: List[CreateCloudCollaborator]
36
+ ) -> str:
37
+ """Batch add collaborators to a cloud."""
38
+ return self._private_sdk.add_collaborators(cloud, collaborators)
39
+
40
+
41
+ # Note: indentation here matches that of connect.py::ClientBuilder.
42
+ BUILDER_HELP_FOOTER = """
43
+ See ``anyscale.ClientBuilder`` for full documentation of
44
+ this experimental feature."""
45
+
46
+
47
+ class CloudModule(ModuleType):
48
+ """
49
+ A custom callable module object for `anyscale.cloud`.
50
+
51
+ This hack is needed since `anyscale.cloud` is a function for Anyscale connect but also a module for the SDK.
52
+ """
53
+
54
+ def __init__(self):
55
+ # Expose attributes from the SDK.
56
+ self.CloudSDK = CloudSDK
57
+ self.add_collaborators = add_collaborators
58
+
59
+ # Expose Anyscale connect
60
+ self.new_builder = self._new_builder()
61
+
62
+ # This code is copied from frontend/cli/anyscale/__init__.py.
63
+ def _new_builder(self) -> Any:
64
+ target = ClientBuilder.cloud
65
+
66
+ def new_session_builder(*a: List[Any], **kw: Dict[str, Any]) -> Any:
67
+ builder = ClientBuilder()
68
+ return target(builder, *a, **kw) # type: ignore
69
+
70
+ new_session_builder.__name__ = "cloud"
71
+ new_session_builder.__doc__ = target.__doc__ + BUILDER_HELP_FOOTER # type: ignore
72
+ new_session_builder.__signature__ = inspect.signature(target) # type: ignore
73
+
74
+ return new_session_builder
75
+
76
+ def __call__(self, *args, **kwargs):
77
+ """
78
+ Define the behavior when `anyscale.cloud` is called for Anyscale connect.
79
+ """
80
+ return self.new_builder(*args, **kwargs)
81
+
82
+
83
+ sys.modules[__name__] = CloudModule()
@@ -0,0 +1,25 @@
1
+ from typing import List
2
+
3
+ from anyscale._private.sdk.base_sdk import BaseSDK
4
+ from anyscale.client.openapi_client.models import (
5
+ CreateCloudCollaborator as CreateCloudCollaboratorModel,
6
+ )
7
+ from anyscale.cloud.models import CreateCloudCollaborator
8
+
9
+
10
+ class PrivateCloudSDK(BaseSDK):
11
+ def add_collaborators(
12
+ self, cloud: str, collaborators: List[CreateCloudCollaborator]
13
+ ) -> str:
14
+ cloud_id = self.client.get_cloud_id(cloud_name=cloud, compute_config_id=None)
15
+
16
+ return self.client.add_cloud_collaborators(
17
+ cloud_id=cloud_id,
18
+ collaborators=[
19
+ CreateCloudCollaboratorModel(
20
+ email=collaborator.email,
21
+ permission_level=collaborator.permission_level.lower(),
22
+ )
23
+ for collaborator in collaborators
24
+ ],
25
+ )
@@ -0,0 +1,45 @@
1
+ from typing import List
2
+
3
+ from anyscale._private.sdk import sdk_command
4
+ from anyscale.cloud._private.cloud_sdk import PrivateCloudSDK
5
+ from anyscale.cloud.models import CreateCloudCollaborator
6
+
7
+
8
+ _CLOUD_SDK_SINGLETON_KEY = "cloud_sdk"
9
+
10
+ _ADD_COLLABORATORS_EXAMPLE = """
11
+ import anyscale
12
+ from anyscale.cloud.models import CloudPermissionLevel, CreateCloudCollaborator
13
+
14
+ anyscale.cloud.add_collaborators(
15
+ cloud="cloud_name",
16
+ collaborators=[
17
+ CreateCloudCollaborator(
18
+ email="test1@anyscale.com",
19
+ permission_level=CloudPermissionLevel.WRITE,
20
+ ),
21
+ CreateCloudCollaborator(
22
+ email="test2@anyscale.com",
23
+ permission_level=CloudPermissionLevel.READONLY,
24
+ ),
25
+ ],
26
+ )
27
+ """
28
+
29
+ _ADD_COLLABORATORS_ARG_DOCSTRINGS = {
30
+ "cloud": "The cloud to add users to.",
31
+ "collaborators": "The list of collaborators to add to the cloud.",
32
+ }
33
+
34
+
35
+ @sdk_command(
36
+ _CLOUD_SDK_SINGLETON_KEY,
37
+ PrivateCloudSDK,
38
+ doc_py_example=_ADD_COLLABORATORS_EXAMPLE,
39
+ arg_docstrings=_ADD_COLLABORATORS_ARG_DOCSTRINGS,
40
+ )
41
+ def add_collaborators(
42
+ cloud: str, collaborators: List[CreateCloudCollaborator], *, _sdk: PrivateCloudSDK
43
+ ) -> str:
44
+ """Batch add collaborators to a cloud."""
45
+ return _sdk.add_collaborators(cloud, collaborators)
@@ -0,0 +1,91 @@
1
+ from dataclasses import dataclass, field
2
+ from typing import Any, Dict, List
3
+
4
+ from anyscale._private.models import ModelBase, ModelEnum
5
+
6
+
7
+ class CloudPermissionLevel(ModelEnum):
8
+ WRITE = "WRITE"
9
+ READONLY = "READONLY"
10
+
11
+ __docstrings__ = {
12
+ WRITE: "Write permission level for the cloud",
13
+ READONLY: "Readonly permission level for the cloud",
14
+ }
15
+
16
+
17
+ @dataclass(frozen=True)
18
+ class CreateCloudCollaborator(ModelBase):
19
+ """User to be added as a collaborator to a cloud.
20
+ """
21
+
22
+ __doc_py_example__ = """\
23
+ import anyscale
24
+ from anyscale.cloud.models import CloudPermissionLevel, CreateCloudCollaborator
25
+
26
+ create_cloud_collaborator = CreateCloudCollaborator(
27
+ # Email of the user to be added as a collaborator
28
+ email="test@anyscale.com",
29
+ # Permission level for the user to the cloud (CloudPermissionLevel.WRITE, CloudPermissionLevel.READONLY)
30
+ permission_level=CloudPermissionLevel.READONLY,
31
+ )
32
+ """
33
+
34
+ def _validate_email(self, email: str):
35
+ if not isinstance(email, str):
36
+ raise TypeError("Email must be a string.")
37
+
38
+ email: str = field(
39
+ metadata={"docstring": "Email of the user to be added as a collaborator."},
40
+ )
41
+
42
+ def _validate_permission_level(
43
+ self, permission_level: CloudPermissionLevel
44
+ ) -> CloudPermissionLevel:
45
+ if isinstance(permission_level, str):
46
+ return CloudPermissionLevel.validate(permission_level)
47
+ elif isinstance(permission_level, CloudPermissionLevel):
48
+ return permission_level
49
+ else:
50
+ raise TypeError(
51
+ f"'permission_level' must be a 'CloudPermissionLevel' (it is {type(permission_level)})."
52
+ )
53
+
54
+ permission_level: CloudPermissionLevel = field( # type: ignore
55
+ default=CloudPermissionLevel.READONLY, # type: ignore
56
+ metadata={
57
+ "docstring": "Permission level the added user should have for the cloud" # type: ignore
58
+ f"(one of: {','.join([str(m.value) for m in CloudPermissionLevel])}", # type: ignore
59
+ },
60
+ )
61
+
62
+
63
+ @dataclass(frozen=True)
64
+ class CreateCloudCollaborators(ModelBase):
65
+ """List of users to be added as collaborators to a cloud.
66
+ """
67
+
68
+ __doc_py_example__ = """\
69
+ import anyscale
70
+ from anyscale.cloud.models import CloudPermissionLevel, CreateCloudCollaborator, CreateCloudCollaborators
71
+
72
+ create_cloud_collaborator = CreateCloudCollaborator(
73
+ # Email of the user to be added as a collaborator
74
+ email="test@anyscale.com",
75
+ # Permission level for the user to the cloud (CloudPermissionLevel.WRITE, CloudPermissionLevel.READONLY)
76
+ permission_level=CloudPermissionLevel.READONLY,
77
+ )
78
+ create_cloud_collaborators = CreateCloudCollaborators(
79
+ collaborators=[create_cloud_collaborator]
80
+ )
81
+ """
82
+
83
+ collaborators: List[Dict[str, Any]] = field(
84
+ metadata={
85
+ "docstring": "List of users to be added as collaborators to a cloud."
86
+ },
87
+ )
88
+
89
+ def _validate_collaborators(self, collaborators: List[Dict[str, Any]]):
90
+ if not isinstance(collaborators, list):
91
+ raise TypeError("Collaborators must be a list.")
@@ -4,7 +4,7 @@ from typing import Optional, Tuple
4
4
  from anyscale.authenticate import get_auth_api_client
5
5
  from anyscale.cli_logger import BlockLogger
6
6
  from anyscale.client.openapi_client.api.default_api import DefaultApi
7
- from anyscale.cloud import get_cloud_id_and_name, get_last_used_cloud
7
+ from anyscale.cloud_utils import get_cloud_id_and_name, get_last_used_cloud
8
8
  from anyscale.sdk.anyscale_client import (
9
9
  ArchiveStatus,
10
10
  ComputeTemplateConfig,
@@ -26,13 +26,13 @@ def aggregated_instance_usage_cli() -> None:
26
26
  "--start-date",
27
27
  required=True,
28
28
  type=str,
29
- help="The start date of the aggregated instance usage report (inclusive). Format: YYYY-MM-DD",
29
+ help="The start date (inclusive) of the aggregated instance usage report. Format: YYYY-MM-DD",
30
30
  )
31
31
  @click.option(
32
32
  "--end-date",
33
33
  required=True,
34
34
  type=str,
35
- help="The end date of the aggregated instance usage report (inclusive). Format: YYYY-MM-DD",
35
+ help="The end date (inclusive) of the aggregated instance usage report. Format: YYYY-MM-DD",
36
36
  )
37
37
  @click.option(
38
38
  "--cloud",
@@ -50,7 +50,7 @@ def aggregated_instance_usage_cli() -> None:
50
50
  "--directory",
51
51
  required=False,
52
52
  type=str,
53
- help="The directory to save the CSV file to. Default is the current directory",
53
+ help="The directory to save the CSV file to. Default is the current directory.",
54
54
  )
55
55
  @click.option(
56
56
  "--hide-progress-bar",
@@ -67,7 +67,7 @@ def download_csv(
67
67
  hide_progress_bar: Optional[bool] = False,
68
68
  ) -> None:
69
69
  """
70
- Download an aggregated instance usage report as a zipped csv to the provided directory.
70
+ Download an aggregated instance usage report as a zipped CSV to the provided directory.
71
71
  """
72
72
  log.info("Downloading aggregated instance usage CSV...")
73
73
  try:
@@ -3,11 +3,13 @@ from typing import List, Optional
3
3
 
4
4
  import click
5
5
 
6
+ import anyscale
6
7
  from anyscale.cli_logger import BlockLogger
7
8
  from anyscale.client.openapi_client.models import ClusterManagementStackVersions
8
9
  from anyscale.client.openapi_client.models.compute_stack import ComputeStack
9
- from anyscale.commands import cloud_commands_util
10
- from anyscale.commands.util import OptionPromptNull
10
+ from anyscale.cloud.models import CreateCloudCollaborator, CreateCloudCollaborators
11
+ from anyscale.commands import cloud_commands_util, command_examples
12
+ from anyscale.commands.util import AnyscaleCommand, OptionPromptNull
11
13
  from anyscale.controllers.cloud_controller import CloudController
12
14
  from anyscale.util import validate_non_negative_arg
13
15
 
@@ -1017,3 +1019,37 @@ def cloud_edit( # noqa: PLR0913
1017
1019
  raise click.ClickException(
1018
1020
  "Please provide at least one of the following arguments: --aws-s3-id, --aws-efs-id, --aws-efs-mount-target-ip, --memorydb-cluster-id, --gcp-filestore-instance-id, --gcp-filestore-location, --gcp-cloud-storage-bucket-name, --memorystore-instance-name, --enable-auto-add-user, --disable-auto-add-user."
1019
1021
  )
1022
+
1023
+
1024
+ @cloud_cli.command(
1025
+ name="add-collaborators",
1026
+ help="Add collaborators to the cloud.",
1027
+ cls=AnyscaleCommand,
1028
+ example=command_examples.CLOUD_ADD_COLLABORATORS_EXAMPLE,
1029
+ )
1030
+ @click.option(
1031
+ "--cloud", "-c", help="Name of the cloud to add collaborators to.", required=True
1032
+ )
1033
+ @click.option(
1034
+ "--users-file",
1035
+ help="Path to a YAML file containing a list of users to add to the cloud.",
1036
+ required=True,
1037
+ )
1038
+ def add_collaborators(cloud: str, users_file: str,) -> None:
1039
+ collaborators = CreateCloudCollaborators.from_yaml(users_file)
1040
+
1041
+ try:
1042
+ anyscale.cloud.add_collaborators(
1043
+ cloud=cloud,
1044
+ collaborators=[
1045
+ CreateCloudCollaborator(**collaborator)
1046
+ for collaborator in collaborators.collaborators
1047
+ ],
1048
+ )
1049
+ except ValueError as e:
1050
+ log.error(f"Error adding collaborators to cloud: {e}")
1051
+ return
1052
+
1053
+ log.info(
1054
+ f"Successfully added {len(collaborators.collaborators)} collaborators to cloud {cloud}."
1055
+ )
@@ -217,6 +217,10 @@ $ anyscale machine-pool update --name can-testing --spec-file spec.yaml
217
217
  Updated machine pool 'can-testing'.
218
218
  """
219
219
 
220
+ MACHINE_POOL_DESCRIBE_EXAMPLE = """\
221
+ $ anyscale machine-pool describe --name can-testing --mode machines
222
+ """
223
+
220
224
  MACHINE_POOL_DELETE_EXAMPLE = """\
221
225
  $ anyscale machine-pool delete --name can-testing
222
226
  Deleted machine pool 'can-testing'.
@@ -498,4 +502,61 @@ USER_BATCH_CREATE_EXAMPLE = """\
498
502
  $ anyscale user batch-create --users-file users.yaml
499
503
  (anyscale +0.5s) Creating users...
500
504
  (anyscale +0.8s) 2 users created.
505
+
506
+ $ cat users_file.yaml
507
+ create_users:
508
+ - name: name1
509
+ email: test1@anyscale.com
510
+ password: ''
511
+ is_sso_user: false
512
+ lastname: lastname1
513
+ title: title1
514
+ - name: name2
515
+ email: test2@anyscale.com
516
+ password: ''
517
+ is_sso_user: false
518
+ lastname: lastname2
519
+ title: title2
520
+ """
521
+
522
+ ORGANIZATION_INVITATION_CREATE_EXAMPLE = """\
523
+ $ anyscale organization-invitation create --emails test1@anyscale.com,test2@anyscale.com
524
+ (anyscale +0.5s) Creating organization invitations...
525
+ (anyscale +1.7s) Organization invitations sent to: test1@anyscale.com, test2@anyscale.com
526
+ """
527
+
528
+ ORGANIZATION_INVITATION_LIST_EXAMPLE = """\
529
+ $ anyscale organization-invitation list
530
+ ID Email Created At Expires At
531
+ ------------- ----------------- ------------------- -------------------
532
+ orginv_abcedf test@anyscale.com 11/25/2024 10:24 PM 12/02/2024 10:24 PM
533
+ """
534
+
535
+ ORGANIZATION_INVITATION_DELETE_EXAMPLE = """\
536
+ $ anyscale organization-invitation delete --email test@anyscale.com
537
+ (anyscale +0.6s) Organization invitation for test@anyscale.com deleted.
538
+ """
539
+
540
+ PROJECT_ADD_COLLABORATORS_EXAMPLE = """\
541
+ $ anyscale project add-collaborators --cloud cloud_name --project project_name --users-file collaborators.yaml
542
+ (anyscale +1.3s) Successfully added 3 collaborators to project project_name.
543
+ $ cat collaborators.yaml
544
+ collaborators:
545
+ - email: "test1@anyscale.com"
546
+ permission_level: "write"
547
+ - email: "test2@anyscale.com"
548
+ permission_level: "readonly"
549
+ - email: "test3@anyscale.com"
550
+ permission_level: "owner"
551
+ """
552
+
553
+ CLOUD_ADD_COLLABORATORS_EXAMPLE = """\
554
+ $ anyscale cloud add-collaborators --cloud cloud_name --users-file collaborators.yaml
555
+ (anyscale +1.3s) Successfully added 2 collaborators to cloud cloud_name.
556
+ $ cat collaborators.yaml
557
+ collaborators:
558
+ - email: "test1@anyscale.com"
559
+ permission_level: "write"
560
+ - email: "test2@anyscale.com"
561
+ permission_level: "readonly"
501
562
  """
@@ -2,7 +2,7 @@ from io import StringIO
2
2
  from json import dumps as json_dumps
3
3
  import pathlib
4
4
  from subprocess import list2cmdline
5
- from typing import Optional, Tuple
5
+ from typing import List, Optional, Tuple
6
6
 
7
7
  import click
8
8
  import yaml
@@ -10,6 +10,7 @@ import yaml
10
10
  import anyscale
11
11
  from anyscale._private.models.image_uri import ImageURI
12
12
  from anyscale.cli_logger import BlockLogger
13
+ from anyscale.client.openapi_client.models.ha_job_states import HaJobStates
13
14
  from anyscale.commands import command_examples
14
15
  from anyscale.commands.util import (
15
16
  AnyscaleCommand,
@@ -18,7 +19,7 @@ from anyscale.commands.util import (
18
19
  )
19
20
  from anyscale.controllers.job_controller import JobController
20
21
  from anyscale.job.models import JobConfig, JobLogMode, JobState, JobStatus
21
- from anyscale.util import validate_non_negative_arg
22
+ from anyscale.util import validate_list_jobs_state_filter, validate_non_negative_arg
22
23
 
23
24
 
24
25
  log = BlockLogger() # CLI Logger
@@ -118,7 +119,7 @@ def job_cli() -> None:
118
119
  required=False,
119
120
  default=None,
120
121
  type=str,
121
- help="Path to a requirements.txt file containing dependencies for the job. These will be installed on top of the image. When running in a workspace, this defaults to the workspace dependencies.",
122
+ help="Path to a requirements.txt file containing dependencies for the job. Anyscale installs these dependencies on top of the image. If you run a job from a workspace, the default is to use the workspace dependencies, but specifying this option overrides them.",
122
123
  )
123
124
  @click.option(
124
125
  "--py-module",
@@ -386,6 +387,15 @@ and override the entrypoint with `python main.py`.
386
387
  help="Max items to show in list.",
387
388
  callback=validate_non_negative_arg,
388
389
  )
390
+ @click.option(
391
+ "--state",
392
+ "-s",
393
+ "states",
394
+ required=False,
395
+ multiple=True,
396
+ help=f"Filter jobs by state. Accepts one or more states. Allowed states: {', '.join(HaJobStates.allowable_values)}",
397
+ callback=validate_list_jobs_state_filter,
398
+ )
389
399
  def list( # noqa: A001
390
400
  name: Optional[str],
391
401
  id: Optional[str], # noqa: A002
@@ -393,6 +403,7 @@ def list( # noqa: A001
393
403
  include_all_users: bool,
394
404
  include_archived: bool,
395
405
  max_items: int,
406
+ states: List[str],
396
407
  ) -> None:
397
408
  job_controller = JobController()
398
409
  job_controller.list(
@@ -402,6 +413,7 @@ def list( # noqa: A001
402
413
  include_all_users=include_all_users,
403
414
  include_archived=include_archived,
404
415
  max_items=max_items,
416
+ states=states,
405
417
  )
406
418
 
407
419