anyscale 0.24.88__py3-none-any.whl → 0.24.91__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 +1 -1
- 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.24.91.dist-info}/METADATA +1 -5
- {anyscale-0.24.88.dist-info → anyscale-0.24.91.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.24.91.dist-info}/LICENSE +0 -0
- {anyscale-0.24.88.dist-info → anyscale-0.24.91.dist-info}/NOTICE +0 -0
- {anyscale-0.24.88.dist-info → anyscale-0.24.91.dist-info}/WHEEL +0 -0
- {anyscale-0.24.88.dist-info → anyscale-0.24.91.dist-info}/entry_points.txt +0 -0
- {anyscale-0.24.88.dist-info → anyscale-0.24.91.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)
|
anyscale/cloud/models.py
ADDED
|
@@ -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.")
|
anyscale/cluster_compute.py
CHANGED
|
@@ -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.
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
10
|
-
from anyscale.commands
|
|
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.
|
|
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
|
|