anyscale 0.25.11__py3-none-any.whl → 0.26.1__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/_private/anyscale_client/anyscale_client.py +16 -3
- anyscale/_private/anyscale_client/common.py +13 -0
- anyscale/_private/anyscale_client/fake_anyscale_client.py +9 -0
- anyscale/_private/docgen/__main__.py +17 -2
- anyscale/client/README.md +11 -10
- anyscale/client/openapi_client/__init__.py +6 -6
- anyscale/client/openapi_client/api/default_api.py +210 -83
- anyscale/client/openapi_client/models/__init__.py +6 -6
- anyscale/client/openapi_client/models/aggregated_instance_usage_csv.py +81 -3
- anyscale/client/openapi_client/models/create_organization_configuration.py +3 -3
- anyscale/client/openapi_client/models/{create_resource_alert.py → create_resource_notification.py} +40 -40
- anyscale/client/openapi_client/models/{list_resource_alerts_query.py → list_resource_notifications_query.py} +24 -24
- anyscale/client/openapi_client/models/{customer_billing_type.py → machine_launch_failure.py} +65 -14
- anyscale/client/openapi_client/models/organization_configuration.py +3 -3
- anyscale/client/openapi_client/models/organization_configuration_response.py +3 -3
- anyscale/client/openapi_client/models/resource_alert_event_type.py +4 -1
- anyscale/client/openapi_client/models/{resource_alert.py → resource_notification.py} +64 -64
- anyscale/client/openapi_client/models/{resourcealert_list_response.py → resourcenotification_list_response.py} +15 -15
- anyscale/client/openapi_client/models/{resourcealert_response.py → resourcenotification_response.py} +11 -11
- anyscale/client/openapi_client/models/scheduler_info.py +32 -3
- anyscale/client/openapi_client/models/tool.py +2 -1
- anyscale/client/openapi_client/models/workspace_dataplane_proxied_artifacts.py +31 -3
- anyscale/cloud/__init__.py +44 -5
- anyscale/cloud/_private/cloud_sdk.py +59 -2
- anyscale/cloud/commands.py +72 -4
- anyscale/cloud/models.py +121 -3
- anyscale/commands/cloud_commands.py +76 -0
- anyscale/commands/command_examples.py +22 -0
- anyscale/commands/machine_pool_commands.py +15 -10
- anyscale/resource_quota/commands.py +1 -2
- anyscale/service/_private/service_sdk.py +2 -3
- anyscale/shared_anyscale_utils/constants.py +3 -0
- anyscale/version.py +1 -1
- {anyscale-0.25.11.dist-info → anyscale-0.26.1.dist-info}/METADATA +1 -1
- {anyscale-0.25.11.dist-info → anyscale-0.26.1.dist-info}/RECORD +40 -39
- {anyscale-0.25.11.dist-info → anyscale-0.26.1.dist-info}/LICENSE +0 -0
- {anyscale-0.25.11.dist-info → anyscale-0.26.1.dist-info}/NOTICE +0 -0
- {anyscale-0.25.11.dist-info → anyscale-0.26.1.dist-info}/WHEEL +0 -0
- {anyscale-0.25.11.dist-info → anyscale-0.26.1.dist-info}/entry_points.txt +0 -0
- {anyscale-0.25.11.dist-info → anyscale-0.26.1.dist-info}/top_level.txt +0 -0
@@ -34,15 +34,17 @@ class SchedulerInfo(object):
|
|
34
34
|
"""
|
35
35
|
openapi_types = {
|
36
36
|
'requests': 'list[RequestStateInfo]',
|
37
|
-
'machines': 'list[MachineStateInfo]'
|
37
|
+
'machines': 'list[MachineStateInfo]',
|
38
|
+
'recent_launch_failures': 'list[MachineLaunchFailure]'
|
38
39
|
}
|
39
40
|
|
40
41
|
attribute_map = {
|
41
42
|
'requests': 'requests',
|
42
|
-
'machines': 'machines'
|
43
|
+
'machines': 'machines',
|
44
|
+
'recent_launch_failures': 'recent_launch_failures'
|
43
45
|
}
|
44
46
|
|
45
|
-
def __init__(self, requests=None, machines=None, local_vars_configuration=None): # noqa: E501
|
47
|
+
def __init__(self, requests=None, machines=None, recent_launch_failures=None, local_vars_configuration=None): # noqa: E501
|
46
48
|
"""SchedulerInfo - a model defined in OpenAPI""" # noqa: E501
|
47
49
|
if local_vars_configuration is None:
|
48
50
|
local_vars_configuration = Configuration()
|
@@ -50,10 +52,12 @@ class SchedulerInfo(object):
|
|
50
52
|
|
51
53
|
self._requests = None
|
52
54
|
self._machines = None
|
55
|
+
self._recent_launch_failures = None
|
53
56
|
self.discriminator = None
|
54
57
|
|
55
58
|
self.requests = requests
|
56
59
|
self.machines = machines
|
60
|
+
self.recent_launch_failures = recent_launch_failures
|
57
61
|
|
58
62
|
@property
|
59
63
|
def requests(self):
|
@@ -105,6 +109,31 @@ class SchedulerInfo(object):
|
|
105
109
|
|
106
110
|
self._machines = machines
|
107
111
|
|
112
|
+
@property
|
113
|
+
def recent_launch_failures(self):
|
114
|
+
"""Gets the recent_launch_failures of this SchedulerInfo. # noqa: E501
|
115
|
+
|
116
|
+
Recent launch failures. # noqa: E501
|
117
|
+
|
118
|
+
:return: The recent_launch_failures of this SchedulerInfo. # noqa: E501
|
119
|
+
:rtype: list[MachineLaunchFailure]
|
120
|
+
"""
|
121
|
+
return self._recent_launch_failures
|
122
|
+
|
123
|
+
@recent_launch_failures.setter
|
124
|
+
def recent_launch_failures(self, recent_launch_failures):
|
125
|
+
"""Sets the recent_launch_failures of this SchedulerInfo.
|
126
|
+
|
127
|
+
Recent launch failures. # noqa: E501
|
128
|
+
|
129
|
+
:param recent_launch_failures: The recent_launch_failures of this SchedulerInfo. # noqa: E501
|
130
|
+
:type: list[MachineLaunchFailure]
|
131
|
+
"""
|
132
|
+
if self.local_vars_configuration.client_side_validation and recent_launch_failures is None: # noqa: E501
|
133
|
+
raise ValueError("Invalid value for `recent_launch_failures`, must not be `None`") # noqa: E501
|
134
|
+
|
135
|
+
self._recent_launch_failures = recent_launch_failures
|
136
|
+
|
108
137
|
def to_dict(self):
|
109
138
|
"""Returns the model properties as a dict"""
|
110
139
|
result = {}
|
@@ -30,8 +30,9 @@ class Tool(object):
|
|
30
30
|
"""
|
31
31
|
SERVICE_DASHBOARD = "service_dashboard"
|
32
32
|
SERVE_DEPLOYMENT_DASHBOARD = "serve_deployment_dashboard"
|
33
|
+
SERVICE_ALERTING_DASHBOARD = "service_alerting_dashboard"
|
33
34
|
|
34
|
-
allowable_values = [SERVICE_DASHBOARD, SERVE_DEPLOYMENT_DASHBOARD] # noqa: E501
|
35
|
+
allowable_values = [SERVICE_DASHBOARD, SERVE_DEPLOYMENT_DASHBOARD, SERVICE_ALERTING_DASHBOARD] # noqa: E501
|
35
36
|
|
36
37
|
"""
|
37
38
|
Attributes:
|
@@ -36,17 +36,19 @@ class WorkspaceDataplaneProxiedArtifacts(object):
|
|
36
36
|
'requirements': 'str',
|
37
37
|
'skip_packages_tracking': 'str',
|
38
38
|
'environment_variables': 'list[str]',
|
39
|
-
'dockerfile': 'str'
|
39
|
+
'dockerfile': 'str',
|
40
|
+
'dockerfile_draft': 'str'
|
40
41
|
}
|
41
42
|
|
42
43
|
attribute_map = {
|
43
44
|
'requirements': 'requirements',
|
44
45
|
'skip_packages_tracking': 'skip_packages_tracking',
|
45
46
|
'environment_variables': 'environment_variables',
|
46
|
-
'dockerfile': 'dockerfile'
|
47
|
+
'dockerfile': 'dockerfile',
|
48
|
+
'dockerfile_draft': 'dockerfile_draft'
|
47
49
|
}
|
48
50
|
|
49
|
-
def __init__(self, requirements=None, skip_packages_tracking=None, environment_variables=None, dockerfile=None, local_vars_configuration=None): # noqa: E501
|
51
|
+
def __init__(self, requirements=None, skip_packages_tracking=None, environment_variables=None, dockerfile=None, dockerfile_draft=None, local_vars_configuration=None): # noqa: E501
|
50
52
|
"""WorkspaceDataplaneProxiedArtifacts - a model defined in OpenAPI""" # noqa: E501
|
51
53
|
if local_vars_configuration is None:
|
52
54
|
local_vars_configuration = Configuration()
|
@@ -56,6 +58,7 @@ class WorkspaceDataplaneProxiedArtifacts(object):
|
|
56
58
|
self._skip_packages_tracking = None
|
57
59
|
self._environment_variables = None
|
58
60
|
self._dockerfile = None
|
61
|
+
self._dockerfile_draft = None
|
59
62
|
self.discriminator = None
|
60
63
|
|
61
64
|
if requirements is not None:
|
@@ -66,6 +69,8 @@ class WorkspaceDataplaneProxiedArtifacts(object):
|
|
66
69
|
self.environment_variables = environment_variables
|
67
70
|
if dockerfile is not None:
|
68
71
|
self.dockerfile = dockerfile
|
72
|
+
if dockerfile_draft is not None:
|
73
|
+
self.dockerfile_draft = dockerfile_draft
|
69
74
|
|
70
75
|
@property
|
71
76
|
def requirements(self):
|
@@ -159,6 +164,29 @@ class WorkspaceDataplaneProxiedArtifacts(object):
|
|
159
164
|
|
160
165
|
self._dockerfile = dockerfile
|
161
166
|
|
167
|
+
@property
|
168
|
+
def dockerfile_draft(self):
|
169
|
+
"""Gets the dockerfile_draft of this WorkspaceDataplaneProxiedArtifacts. # noqa: E501
|
170
|
+
|
171
|
+
The Dockerfile.draft of the workspace. # noqa: E501
|
172
|
+
|
173
|
+
:return: The dockerfile_draft of this WorkspaceDataplaneProxiedArtifacts. # noqa: E501
|
174
|
+
:rtype: str
|
175
|
+
"""
|
176
|
+
return self._dockerfile_draft
|
177
|
+
|
178
|
+
@dockerfile_draft.setter
|
179
|
+
def dockerfile_draft(self, dockerfile_draft):
|
180
|
+
"""Sets the dockerfile_draft of this WorkspaceDataplaneProxiedArtifacts.
|
181
|
+
|
182
|
+
The Dockerfile.draft of the workspace. # noqa: E501
|
183
|
+
|
184
|
+
:param dockerfile_draft: The dockerfile_draft of this WorkspaceDataplaneProxiedArtifacts. # noqa: E501
|
185
|
+
:type: str
|
186
|
+
"""
|
187
|
+
|
188
|
+
self._dockerfile_draft = dockerfile_draft
|
189
|
+
|
162
190
|
def to_dict(self):
|
163
191
|
"""Returns the model properties as a dict"""
|
164
192
|
result = {}
|
anyscale/cloud/__init__.py
CHANGED
@@ -11,9 +11,14 @@ from anyscale.cloud._private.cloud_sdk import PrivateCloudSDK
|
|
11
11
|
from anyscale.cloud.commands import (
|
12
12
|
_ADD_COLLABORATORS_ARG_DOCSTRINGS,
|
13
13
|
_ADD_COLLABORATORS_EXAMPLE,
|
14
|
+
_GET_ARG_DOCSTRINGS,
|
15
|
+
_GET_DEFAULT_EXAMPLE,
|
16
|
+
_GET_EXAMPLE,
|
14
17
|
add_collaborators,
|
18
|
+
get,
|
19
|
+
get_default,
|
15
20
|
)
|
16
|
-
from anyscale.cloud.models import CreateCloudCollaborator
|
21
|
+
from anyscale.cloud.models import Cloud, CreateCloudCollaborator
|
17
22
|
from anyscale.connect import ClientBuilder
|
18
23
|
|
19
24
|
|
@@ -31,12 +36,43 @@ class CloudSDK:
|
|
31
36
|
doc_py_example=_ADD_COLLABORATORS_EXAMPLE,
|
32
37
|
arg_docstrings=_ADD_COLLABORATORS_ARG_DOCSTRINGS,
|
33
38
|
)
|
34
|
-
def add_collaborators(
|
35
|
-
self, cloud: str, collaborators: List[CreateCloudCollaborator]
|
39
|
+
def add_collaborators(
|
40
|
+
self, cloud: str, collaborators: List[CreateCloudCollaborator],
|
36
41
|
) -> str:
|
37
|
-
"""
|
42
|
+
"""
|
43
|
+
Batch add collaborators to a cloud.
|
44
|
+
|
45
|
+
:param cloud: The cloud to add users to.
|
46
|
+
:param collaborators: The list of collaborators to add to the cloud.
|
47
|
+
"""
|
38
48
|
return self._private_sdk.add_collaborators(cloud, collaborators)
|
39
49
|
|
50
|
+
@sdk_docs(
|
51
|
+
doc_py_example=_GET_EXAMPLE, arg_docstrings=_GET_ARG_DOCSTRINGS,
|
52
|
+
)
|
53
|
+
def get_cloud(
|
54
|
+
self, id: Optional[str], name: Optional[str], # noqa: A002
|
55
|
+
) -> Optional[Cloud]:
|
56
|
+
"""
|
57
|
+
Retrieve a cloud by its name or ID.
|
58
|
+
|
59
|
+
:param id: The ID of the cloud to retrieve.
|
60
|
+
:param name: The name of the cloud to retrieve.
|
61
|
+
:return: A ``Cloud`` object if found, otherwise ``None``.
|
62
|
+
"""
|
63
|
+
return self._private_sdk.get(id=id, name=name)
|
64
|
+
|
65
|
+
@sdk_docs(
|
66
|
+
doc_py_example=_GET_DEFAULT_EXAMPLE, arg_docstrings={},
|
67
|
+
)
|
68
|
+
def get_default_cloud(self) -> Optional[Cloud]:
|
69
|
+
"""
|
70
|
+
Get the default cloud for your organization.
|
71
|
+
|
72
|
+
:return: The default ``Cloud`` object if it exists, otherwise ``None``.
|
73
|
+
"""
|
74
|
+
return self._private_sdk.get_default()
|
75
|
+
|
40
76
|
|
41
77
|
# Note: indentation here matches that of connect.py::ClientBuilder.
|
42
78
|
BUILDER_HELP_FOOTER = """
|
@@ -48,13 +84,16 @@ class CloudModule(ModuleType):
|
|
48
84
|
"""
|
49
85
|
A custom callable module object for `anyscale.cloud`.
|
50
86
|
|
51
|
-
This hack is needed since `anyscale.cloud` is a function for Anyscale connect
|
87
|
+
This hack is needed since `anyscale.cloud` is a function for Anyscale connect
|
88
|
+
but also a module for the SDK.
|
52
89
|
"""
|
53
90
|
|
54
91
|
def __init__(self):
|
55
92
|
# Expose attributes from the SDK.
|
56
93
|
self.CloudSDK = CloudSDK
|
57
94
|
self.add_collaborators = add_collaborators
|
95
|
+
self.get = get
|
96
|
+
self.get_default = get_default
|
58
97
|
|
59
98
|
# Expose Anyscale connect
|
60
99
|
self.new_builder = self._new_builder()
|
@@ -1,10 +1,16 @@
|
|
1
|
-
from typing import List
|
1
|
+
from typing import List, Optional
|
2
2
|
|
3
3
|
from anyscale._private.sdk.base_sdk import BaseSDK
|
4
4
|
from anyscale.client.openapi_client.models import (
|
5
|
+
Cloud as CloudModel,
|
5
6
|
CreateCloudCollaborator as CreateCloudCollaboratorModel,
|
6
7
|
)
|
7
|
-
from anyscale.cloud.models import
|
8
|
+
from anyscale.cloud.models import (
|
9
|
+
Cloud,
|
10
|
+
CloudProvider,
|
11
|
+
ComputeStack,
|
12
|
+
CreateCloudCollaborator,
|
13
|
+
)
|
8
14
|
|
9
15
|
|
10
16
|
class PrivateCloudSDK(BaseSDK):
|
@@ -23,3 +29,54 @@ class PrivateCloudSDK(BaseSDK):
|
|
23
29
|
for collaborator in collaborators
|
24
30
|
],
|
25
31
|
)
|
32
|
+
|
33
|
+
def get(
|
34
|
+
self, id: Optional[str], name: Optional[str], # noqa: A002
|
35
|
+
) -> Optional[Cloud]:
|
36
|
+
if (id and name) or (not id and not name):
|
37
|
+
raise ValueError("Provide exactly one of 'id' or 'name'.")
|
38
|
+
|
39
|
+
if id:
|
40
|
+
openapi_cloud = self.client.get_cloud(cloud_id=id)
|
41
|
+
else:
|
42
|
+
assert name is not None, "Name must be provided if id is not."
|
43
|
+
openapi_cloud = self.client.get_cloud_by_name(name=name)
|
44
|
+
|
45
|
+
return self._to_sdk_cloud(openapi_cloud)
|
46
|
+
|
47
|
+
def get_default(self) -> Optional[Cloud]:
|
48
|
+
openapi_cloud = self.client.get_default_cloud()
|
49
|
+
|
50
|
+
return self._to_sdk_cloud(openapi_cloud)
|
51
|
+
|
52
|
+
def _to_sdk_cloud(self, openapi_cloud: Optional["CloudModel"]) -> Optional[Cloud]:
|
53
|
+
if openapi_cloud is None:
|
54
|
+
return None
|
55
|
+
|
56
|
+
# Validate provider, default to UNKNOWN if validation fails
|
57
|
+
if openapi_cloud.provider is not None:
|
58
|
+
try:
|
59
|
+
provider = CloudProvider.validate(openapi_cloud.provider)
|
60
|
+
except ValueError:
|
61
|
+
provider = CloudProvider.UNKNOWN
|
62
|
+
else:
|
63
|
+
provider = CloudProvider.UNKNOWN
|
64
|
+
|
65
|
+
# Validate compute_stack, default to UNKNOWN if validation fails
|
66
|
+
if openapi_cloud.compute_stack is not None:
|
67
|
+
try:
|
68
|
+
compute_stack = ComputeStack.validate(openapi_cloud.compute_stack)
|
69
|
+
except ValueError:
|
70
|
+
compute_stack = ComputeStack.UNKNOWN
|
71
|
+
else:
|
72
|
+
compute_stack = ComputeStack.UNKNOWN
|
73
|
+
|
74
|
+
return Cloud(
|
75
|
+
id=openapi_cloud.id,
|
76
|
+
name=openapi_cloud.name,
|
77
|
+
provider=provider,
|
78
|
+
region=openapi_cloud.region,
|
79
|
+
created_at=openapi_cloud.created_at,
|
80
|
+
is_default=openapi_cloud.is_default,
|
81
|
+
compute_stack=compute_stack,
|
82
|
+
)
|
anyscale/cloud/commands.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
from typing import List
|
1
|
+
from typing import List, Optional
|
2
2
|
|
3
3
|
from anyscale._private.sdk import sdk_command
|
4
|
-
from anyscale.cloud._private.cloud_sdk import PrivateCloudSDK
|
4
|
+
from anyscale.cloud._private.cloud_sdk import Cloud, PrivateCloudSDK
|
5
5
|
from anyscale.cloud.models import CreateCloudCollaborator
|
6
6
|
|
7
7
|
|
@@ -39,7 +39,75 @@ _ADD_COLLABORATORS_ARG_DOCSTRINGS = {
|
|
39
39
|
arg_docstrings=_ADD_COLLABORATORS_ARG_DOCSTRINGS,
|
40
40
|
)
|
41
41
|
def add_collaborators(
|
42
|
-
cloud: str, collaborators: List[CreateCloudCollaborator], *, _sdk: PrivateCloudSDK
|
42
|
+
cloud: str, collaborators: List[CreateCloudCollaborator], *, _sdk: PrivateCloudSDK,
|
43
43
|
) -> str:
|
44
|
-
"""
|
44
|
+
"""
|
45
|
+
Batch add collaborators to a cloud.
|
46
|
+
|
47
|
+
:param cloud: The cloud to add users to.
|
48
|
+
:param collaborators: The list of collaborators to add to the cloud.
|
49
|
+
"""
|
45
50
|
return _sdk.add_collaborators(cloud, collaborators)
|
51
|
+
|
52
|
+
|
53
|
+
_GET_EXAMPLE = """
|
54
|
+
import anyscale
|
55
|
+
|
56
|
+
# Get a cloud by ID
|
57
|
+
cloud_by_id = anyscale.cloud.get(id="cloud_id")
|
58
|
+
|
59
|
+
# Get a cloud by name
|
60
|
+
cloud_by_name = anyscale.cloud.get(name="cloud_name")
|
61
|
+
"""
|
62
|
+
|
63
|
+
_GET_ARG_DOCSTRINGS = {
|
64
|
+
"id": "The ID of the cloud to retrieve.",
|
65
|
+
"name": "The name of the cloud to retrieve.",
|
66
|
+
}
|
67
|
+
|
68
|
+
|
69
|
+
@sdk_command(
|
70
|
+
_CLOUD_SDK_SINGLETON_KEY,
|
71
|
+
PrivateCloudSDK,
|
72
|
+
doc_py_example=_GET_EXAMPLE,
|
73
|
+
arg_docstrings=_GET_ARG_DOCSTRINGS,
|
74
|
+
)
|
75
|
+
def get(
|
76
|
+
id: Optional[str] = None, # noqa: A002
|
77
|
+
name: Optional[str] = None,
|
78
|
+
*,
|
79
|
+
_sdk: PrivateCloudSDK,
|
80
|
+
) -> Optional[Cloud]:
|
81
|
+
"""
|
82
|
+
Get the cloud model for the provided cloud ID or name.
|
83
|
+
|
84
|
+
If neither ID nor name is provided, returns `None`.
|
85
|
+
|
86
|
+
:param id: The ID of the cloud to retrieve.
|
87
|
+
:param name: The name of the cloud to retrieve.
|
88
|
+
:return: A `Cloud` object if found, otherwise `None`.
|
89
|
+
"""
|
90
|
+
return _sdk.get(id=id, name=name)
|
91
|
+
|
92
|
+
|
93
|
+
_GET_DEFAULT_EXAMPLE = """
|
94
|
+
import anyscale
|
95
|
+
|
96
|
+
# Get the user's default cloud
|
97
|
+
default_cloud = anyscale.cloud.get_default()
|
98
|
+
"""
|
99
|
+
|
100
|
+
|
101
|
+
@sdk_command(
|
102
|
+
_CLOUD_SDK_SINGLETON_KEY,
|
103
|
+
PrivateCloudSDK,
|
104
|
+
doc_py_example=_GET_DEFAULT_EXAMPLE,
|
105
|
+
arg_docstrings={},
|
106
|
+
)
|
107
|
+
def get_default(*, _sdk: PrivateCloudSDK,) -> Optional[Cloud]:
|
108
|
+
"""
|
109
|
+
Get the user's default cloud.
|
110
|
+
|
111
|
+
:return: The default `Cloud` object if it exists, otherwise `None`.
|
112
|
+
"""
|
113
|
+
return _sdk.get_default()
|
anyscale/cloud/models.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
from dataclasses import dataclass, field
|
2
|
-
from
|
2
|
+
from datetime import datetime
|
3
|
+
from typing import Any, Dict, List, Optional, Union
|
3
4
|
|
4
5
|
from anyscale._private.models import ModelBase, ModelEnum
|
5
6
|
|
@@ -11,7 +12,7 @@ class CloudPermissionLevel(ModelEnum):
|
|
11
12
|
__docstrings__ = {
|
12
13
|
WRITE: "Write permission level for the cloud",
|
13
14
|
READONLY: "Readonly permission level for the cloud",
|
14
|
-
}
|
15
|
+
} # type: ignore
|
15
16
|
|
16
17
|
|
17
18
|
@dataclass(frozen=True)
|
@@ -43,7 +44,7 @@ create_cloud_collaborator = CreateCloudCollaborator(
|
|
43
44
|
self, permission_level: CloudPermissionLevel
|
44
45
|
) -> CloudPermissionLevel:
|
45
46
|
if isinstance(permission_level, str):
|
46
|
-
return CloudPermissionLevel.validate(permission_level)
|
47
|
+
return CloudPermissionLevel.validate(permission_level) # type: ignore
|
47
48
|
elif isinstance(permission_level, CloudPermissionLevel):
|
48
49
|
return permission_level
|
49
50
|
else:
|
@@ -89,3 +90,120 @@ create_cloud_collaborators = CreateCloudCollaborators(
|
|
89
90
|
def _validate_collaborators(self, collaborators: List[Dict[str, Any]]):
|
90
91
|
if not isinstance(collaborators, list):
|
91
92
|
raise TypeError("Collaborators must be a list.")
|
93
|
+
|
94
|
+
|
95
|
+
class ComputeStack(ModelEnum):
|
96
|
+
UNKNOWN = "UNKNOWN"
|
97
|
+
VM = "VM"
|
98
|
+
K8S = "K8S"
|
99
|
+
|
100
|
+
__docstrings__ = {
|
101
|
+
UNKNOWN: "Unknown compute stack.",
|
102
|
+
VM: "Virtual machine-based compute stack.",
|
103
|
+
K8S: "Kubernetes-based compute stack.",
|
104
|
+
} # type: ignore
|
105
|
+
|
106
|
+
|
107
|
+
class CloudProvider(ModelEnum):
|
108
|
+
UNKNOWN = "UNKNOWN"
|
109
|
+
AWS = "AWS"
|
110
|
+
GCP = "GCP"
|
111
|
+
AZURE = "AZURE"
|
112
|
+
|
113
|
+
__docstrings__ = {
|
114
|
+
UNKNOWN: "Unknown cloud provider.",
|
115
|
+
AWS: "Amazon Web Services.",
|
116
|
+
GCP: "Google Cloud Platform.",
|
117
|
+
AZURE: "Microsoft Azure.",
|
118
|
+
} # type: ignore
|
119
|
+
|
120
|
+
|
121
|
+
@dataclass(frozen=True)
|
122
|
+
class Cloud(ModelBase):
|
123
|
+
"""Minimal Cloud resource model."""
|
124
|
+
|
125
|
+
__doc_py_example__ = """\
|
126
|
+
from datetime import datetime
|
127
|
+
from anyscale.cloud.models import Cloud, CloudProvider, ComputeStack
|
128
|
+
|
129
|
+
cloud = Cloud(
|
130
|
+
name="my-cloud",
|
131
|
+
id="cloud-123",
|
132
|
+
provider="AWS", # This will be validated as CloudProvider.AWS
|
133
|
+
region="us-west-2",
|
134
|
+
created_at=datetime.now(),
|
135
|
+
is_default=True,
|
136
|
+
compute_stack="VM" # This will be validated as ComputeStack.VM
|
137
|
+
)
|
138
|
+
"""
|
139
|
+
|
140
|
+
name: str = field(metadata={"docstring": "Name of this Cloud."})
|
141
|
+
id: str = field(metadata={"docstring": "Unique identifier for this Cloud."})
|
142
|
+
provider: Union[CloudProvider, str] = field(
|
143
|
+
metadata={
|
144
|
+
"docstring": "Cloud provider (AWS, GCP, AZURE) or UNKNOWN if not recognized."
|
145
|
+
},
|
146
|
+
)
|
147
|
+
compute_stack: Union[ComputeStack, str] = field(
|
148
|
+
metadata={
|
149
|
+
"docstring": "The compute stack associated with this cloud's primary cloud resource, or UNKNOWN if not recognized."
|
150
|
+
},
|
151
|
+
)
|
152
|
+
region: Optional[str] = field(
|
153
|
+
default=None, metadata={"docstring": "Region for this Cloud."}
|
154
|
+
)
|
155
|
+
created_at: Optional[datetime] = field(
|
156
|
+
default=None, metadata={"docstring": "When the Cloud was created."}
|
157
|
+
)
|
158
|
+
is_default: Optional[bool] = field(
|
159
|
+
default=None, metadata={"docstring": "Whether this is the default cloud."}
|
160
|
+
)
|
161
|
+
|
162
|
+
def _validate_name(self, name: str) -> str:
|
163
|
+
if not isinstance(name, str) or not name.strip():
|
164
|
+
raise ValueError("name must be a non-empty string")
|
165
|
+
return name
|
166
|
+
|
167
|
+
def _validate_id(self, id: str) -> str: # noqa: A002
|
168
|
+
if not isinstance(id, str) or not id.strip():
|
169
|
+
raise ValueError("id must be a non-empty string")
|
170
|
+
return id
|
171
|
+
|
172
|
+
def _validate_provider(self, provider: Union[CloudProvider, str]) -> CloudProvider:
|
173
|
+
if isinstance(provider, str):
|
174
|
+
# This will raise a ValueError if the provider is unrecognized.
|
175
|
+
provider = CloudProvider(provider)
|
176
|
+
elif not isinstance(provider, CloudProvider):
|
177
|
+
raise TypeError("'provider' must be a CloudProvider.")
|
178
|
+
|
179
|
+
return provider
|
180
|
+
|
181
|
+
def _validate_region(self, region: Optional[str]) -> Optional[str]:
|
182
|
+
if region is not None and not isinstance(region, str):
|
183
|
+
raise TypeError("region must be a string")
|
184
|
+
return region
|
185
|
+
|
186
|
+
def _validate_created_at(
|
187
|
+
self, created_at: Optional[datetime]
|
188
|
+
) -> Optional[datetime]:
|
189
|
+
if created_at is None:
|
190
|
+
return None
|
191
|
+
if not isinstance(created_at, datetime):
|
192
|
+
raise TypeError("created_at must be a datetime object")
|
193
|
+
return created_at
|
194
|
+
|
195
|
+
def _validate_is_default(self, is_default: Optional[bool]) -> Optional[bool]:
|
196
|
+
if is_default is not None and not isinstance(is_default, bool):
|
197
|
+
raise TypeError("is_default must be a bool")
|
198
|
+
return is_default
|
199
|
+
|
200
|
+
def _validate_compute_stack(
|
201
|
+
self, compute_stack: Union[ComputeStack, str]
|
202
|
+
) -> ComputeStack:
|
203
|
+
if isinstance(compute_stack, str):
|
204
|
+
# This will raise a ValueError if the compute_stack is unrecognized.
|
205
|
+
compute_stack = ComputeStack(compute_stack)
|
206
|
+
elif not isinstance(compute_stack, ComputeStack):
|
207
|
+
raise TypeError("'compute_stack' must be a ComputeStack.")
|
208
|
+
|
209
|
+
return compute_stack
|
@@ -1090,3 +1090,79 @@ def add_collaborators(cloud: str, users_file: str,) -> None:
|
|
1090
1090
|
log.info(
|
1091
1091
|
f"Successfully added {len(collaborators.collaborators)} collaborators to cloud {cloud}."
|
1092
1092
|
)
|
1093
|
+
|
1094
|
+
|
1095
|
+
@cloud_cli.command(
|
1096
|
+
name="get",
|
1097
|
+
help="Get information about a specific cloud.",
|
1098
|
+
cls=AnyscaleCommand,
|
1099
|
+
example=command_examples.CLOUD_GET_CLOUD_EXAMPLE,
|
1100
|
+
)
|
1101
|
+
@click.option(
|
1102
|
+
"--name",
|
1103
|
+
"-n",
|
1104
|
+
help="Name of the cloud to get information about.",
|
1105
|
+
type=str,
|
1106
|
+
required=False,
|
1107
|
+
)
|
1108
|
+
@click.option(
|
1109
|
+
"--cloud-id",
|
1110
|
+
"--id",
|
1111
|
+
help="ID of the cloud to get information about.",
|
1112
|
+
type=str,
|
1113
|
+
required=False,
|
1114
|
+
)
|
1115
|
+
def get_cloud(cloud_id: Optional[str], name: Optional[str]) -> None:
|
1116
|
+
"""
|
1117
|
+
Retrieve a cloud by its name or ID and display its details.
|
1118
|
+
|
1119
|
+
:param cloud_id: The ID of the cloud to retrieve.
|
1120
|
+
:param name: The name of the cloud to retrieve.
|
1121
|
+
"""
|
1122
|
+
# Validate that exactly one of --name or --cloud-id is provided
|
1123
|
+
if (cloud_id and name) or (not cloud_id and not name):
|
1124
|
+
log.error("Please provide exactly one of --name or --cloud-id.")
|
1125
|
+
return
|
1126
|
+
|
1127
|
+
try:
|
1128
|
+
cloud = anyscale.cloud.get(id=cloud_id, name=name)
|
1129
|
+
|
1130
|
+
if not cloud:
|
1131
|
+
log.error("Cloud not found.")
|
1132
|
+
return
|
1133
|
+
|
1134
|
+
cloud_dict = cloud.to_dict() if hasattr(cloud, "to_dict") else cloud.__dict__
|
1135
|
+
|
1136
|
+
print(yaml.dump(cloud_dict, sort_keys=False))
|
1137
|
+
|
1138
|
+
except ValueError as e:
|
1139
|
+
log.error(f"Error retrieving cloud: {e}")
|
1140
|
+
|
1141
|
+
|
1142
|
+
@cloud_cli.command(
|
1143
|
+
name="get-default",
|
1144
|
+
help="Get the default cloud for your organization.",
|
1145
|
+
cls=AnyscaleCommand,
|
1146
|
+
example=command_examples.CLOUD_GET_DEFAULT_CLOUD_EXAMPLE,
|
1147
|
+
)
|
1148
|
+
def get_default_cloud() -> None:
|
1149
|
+
"""
|
1150
|
+
Retrieve and display the default cloud configured for your organization.
|
1151
|
+
"""
|
1152
|
+
try:
|
1153
|
+
default_cloud = anyscale.cloud.get_default()
|
1154
|
+
|
1155
|
+
if not default_cloud:
|
1156
|
+
log.error("No default cloud found.")
|
1157
|
+
return
|
1158
|
+
|
1159
|
+
cloud_dict = (
|
1160
|
+
default_cloud.to_dict()
|
1161
|
+
if hasattr(default_cloud, "to_dict")
|
1162
|
+
else default_cloud.__dict__
|
1163
|
+
)
|
1164
|
+
|
1165
|
+
print(yaml.dump(cloud_dict, sort_keys=False))
|
1166
|
+
|
1167
|
+
except ValueError as e:
|
1168
|
+
log.error(f"Error retrieving default cloud: {e}")
|
@@ -561,6 +561,28 @@ collaborators:
|
|
561
561
|
permission_level: "readonly"
|
562
562
|
"""
|
563
563
|
|
564
|
+
CLOUD_GET_CLOUD_EXAMPLE = """\
|
565
|
+
$ anyscale cloud get --name cloud_name
|
566
|
+
name: anyscale_v2_default_cloud
|
567
|
+
id: cld_123
|
568
|
+
provider: AWS
|
569
|
+
region: us-west-2
|
570
|
+
created_at: 2022-10-18 05:12:13.335803+00:00
|
571
|
+
is_default: false
|
572
|
+
compute_stack: VM
|
573
|
+
"""
|
574
|
+
|
575
|
+
CLOUD_GET_DEFAULT_CLOUD_EXAMPLE = """\
|
576
|
+
$ anyscale cloud get-default
|
577
|
+
name: anyscale_v2_default_cloud
|
578
|
+
id: cld_abc
|
579
|
+
provider: AWS
|
580
|
+
region: us-west-2
|
581
|
+
created_at: 2022-10-18 05:12:13.335803+00:00
|
582
|
+
is_default: true
|
583
|
+
compute_stack: VM
|
584
|
+
"""
|
585
|
+
|
564
586
|
SERVICE_ARCHIVE_EXAMPLE = """\
|
565
587
|
$ anyscale service archive --name my_service
|
566
588
|
"""
|