anyscale 0.25.11__py3-none-any.whl → 0.26.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- anyscale/_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/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/version.py +1 -1
- {anyscale-0.25.11.dist-info → anyscale-0.26.0.dist-info}/METADATA +1 -1
- {anyscale-0.25.11.dist-info → anyscale-0.26.0.dist-info}/RECORD +18 -18
- {anyscale-0.25.11.dist-info → anyscale-0.26.0.dist-info}/LICENSE +0 -0
- {anyscale-0.25.11.dist-info → anyscale-0.26.0.dist-info}/NOTICE +0 -0
- {anyscale-0.25.11.dist-info → anyscale-0.26.0.dist-info}/WHEEL +0 -0
- {anyscale-0.25.11.dist-info → anyscale-0.26.0.dist-info}/entry_points.txt +0 -0
- {anyscale-0.25.11.dist-info → anyscale-0.26.0.dist-info}/top_level.txt +0 -0
@@ -546,9 +546,7 @@ class AnyscaleClient(AnyscaleClientInterface):
|
|
546
546
|
@handle_api_exceptions
|
547
547
|
def get_cloud(self, *, cloud_id: str) -> Optional[Cloud]:
|
548
548
|
try:
|
549
|
-
cloud: Cloud = self.
|
550
|
-
cloud_id
|
551
|
-
).result
|
549
|
+
cloud: Cloud = self._external_api_client.get_cloud(cloud_id=cloud_id).result
|
552
550
|
return cloud
|
553
551
|
except InternalApiException as e:
|
554
552
|
if e.status == 404:
|
@@ -556,6 +554,21 @@ class AnyscaleClient(AnyscaleClientInterface):
|
|
556
554
|
|
557
555
|
raise e from None
|
558
556
|
|
557
|
+
@handle_api_exceptions
|
558
|
+
def get_cloud_by_name(self, *, name) -> Optional[Cloud]:
|
559
|
+
cloud_id = self.get_cloud_id(cloud_name=name)
|
560
|
+
return self.get_cloud(cloud_id=cloud_id)
|
561
|
+
|
562
|
+
@handle_api_exceptions
|
563
|
+
def get_default_cloud(self) -> Optional[Cloud]:
|
564
|
+
try:
|
565
|
+
return self._external_api_client.get_default_cloud().result
|
566
|
+
except InternalApiException as e:
|
567
|
+
if e.status == 404:
|
568
|
+
return None
|
569
|
+
|
570
|
+
raise e from None
|
571
|
+
|
559
572
|
@handle_api_exceptions
|
560
573
|
def add_cloud_collaborators(
|
561
574
|
self, cloud_id: str, collaborators: List[CreateCloudCollaborator]
|
@@ -150,6 +150,19 @@ class AnyscaleClientInterface(ABC):
|
|
150
150
|
"""
|
151
151
|
raise NotImplementedError
|
152
152
|
|
153
|
+
@abstractmethod
|
154
|
+
def get_cloud_by_name(self, *, name: str) -> Optional[Cloud]:
|
155
|
+
"""Get the cloud model for the provided cloud name.
|
156
|
+
|
157
|
+
Returns `None` if the cloud name was not found.
|
158
|
+
"""
|
159
|
+
raise NotImplementedError
|
160
|
+
|
161
|
+
@abstractmethod
|
162
|
+
def get_default_cloud(self) -> Optional[Cloud]:
|
163
|
+
"""Get the user's default cloud."""
|
164
|
+
raise NotImplementedError
|
165
|
+
|
153
166
|
@abstractmethod
|
154
167
|
def add_cloud_collaborators(
|
155
168
|
self, cloud_id: str, collaborators: List[CreateCloudCollaborator]
|
@@ -368,6 +368,15 @@ class FakeAnyscaleClient(AnyscaleClientInterface):
|
|
368
368
|
def get_cloud(self, *, cloud_id: str) -> Optional[Cloud]:
|
369
369
|
return self._clouds.get(cloud_id, None)
|
370
370
|
|
371
|
+
def get_cloud_by_name(self, *, name: str) -> Optional[Cloud]:
|
372
|
+
for c in self._clouds.values():
|
373
|
+
if c.name == name:
|
374
|
+
return c
|
375
|
+
return None
|
376
|
+
|
377
|
+
def get_default_cloud(self) -> Optional[Cloud]:
|
378
|
+
return self._clouds.get(self.DEFAULT_CLOUD_ID, None)
|
379
|
+
|
371
380
|
def add_cloud_collaborators(
|
372
381
|
self, cloud_id: str, collaborators: List[CreateCloudCollaborator]
|
373
382
|
) -> None:
|
@@ -11,7 +11,10 @@ from anyscale import scripts
|
|
11
11
|
from anyscale._private.docgen.generator import MarkdownGenerator, Module
|
12
12
|
from anyscale.aggregated_instance_usage.models import DownloadCSVFilters
|
13
13
|
from anyscale.cloud.models import (
|
14
|
+
Cloud,
|
14
15
|
CloudPermissionLevel,
|
16
|
+
CloudProvider,
|
17
|
+
ComputeStack,
|
15
18
|
CreateCloudCollaborator,
|
16
19
|
)
|
17
20
|
from anyscale.commands import (
|
@@ -443,10 +446,22 @@ ALL_MODULES = [
|
|
443
446
|
cloud_commands.cloud_config_update,
|
444
447
|
cloud_commands.cloud_set_default,
|
445
448
|
cloud_commands.add_collaborators,
|
449
|
+
cloud_commands.get_cloud,
|
450
|
+
cloud_commands.get_default_cloud,
|
446
451
|
],
|
447
452
|
sdk_prefix="anyscale.cloud",
|
448
|
-
sdk_commands=[
|
449
|
-
|
453
|
+
sdk_commands=[
|
454
|
+
anyscale.cloud.add_collaborators,
|
455
|
+
anyscale.cloud.get,
|
456
|
+
anyscale.cloud.get_default,
|
457
|
+
],
|
458
|
+
models=[
|
459
|
+
CloudPermissionLevel,
|
460
|
+
CreateCloudCollaborator,
|
461
|
+
Cloud,
|
462
|
+
ComputeStack,
|
463
|
+
CloudProvider,
|
464
|
+
],
|
450
465
|
cli_command_group_prefix={cloud_commands.cloud_config_update: "config"},
|
451
466
|
legacy_sdk_commands={
|
452
467
|
# limited support, no replacement yet
|
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
|
"""
|
anyscale/version.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__version__ = "0.
|
1
|
+
__version__ = "0.26.0"
|
@@ -27,15 +27,15 @@ anyscale/scripts.py,sha256=HR6JOCBVBXMVi1Kz5uJmjsh73t2l1W8UbUge83ofnqk,5474
|
|
27
27
|
anyscale/snapshot.py,sha256=UGJT5C1s_4xmQxjWODK5DFpGxHRBX5jOCdSCqXESH8E,1685
|
28
28
|
anyscale/tables.py,sha256=TV4F2uLnwehvbkAfaP7iuLlT2wLIo6ORH2LVdRGXW5g,2840
|
29
29
|
anyscale/util.py,sha256=Tqb9qWSxQI_PBJVSDxm9RWqFyGJFClgZDPByhb_fhU8,40813
|
30
|
-
anyscale/version.py,sha256=
|
30
|
+
anyscale/version.py,sha256=S1dNBNSmHJkAHkq1vL1Yg-dFpQk0fri_CstNlL1u5qw,23
|
31
31
|
anyscale/workspace_utils.py,sha256=OViE88CnIF5ruVxd3kazQ0Mf2BxqtMq6wx-XQ5A2cp8,1204
|
32
32
|
anyscale/_private/anyscale_client/README.md,sha256=gk8obk7kqg6VWoUHcqDMwJULh35tYKEZFC0UF_dixGA,718
|
33
33
|
anyscale/_private/anyscale_client/__init__.py,sha256=807Blx3RHQeS8BmKZcsOQQ4dYoKlCnpm6Bdsif2CrHg,337
|
34
|
-
anyscale/_private/anyscale_client/anyscale_client.py,sha256=
|
35
|
-
anyscale/_private/anyscale_client/common.py,sha256=
|
36
|
-
anyscale/_private/anyscale_client/fake_anyscale_client.py,sha256=
|
34
|
+
anyscale/_private/anyscale_client/anyscale_client.py,sha256=CDjvf3Ke-scfMEJ1I17d0r5iOeIKlTUUSFHPehs-izs,80107
|
35
|
+
anyscale/_private/anyscale_client/common.py,sha256=4YRbGxX5eVM9WfFNw9o57ZVOGdN1tNAEr2P1frkIYgk,24130
|
36
|
+
anyscale/_private/anyscale_client/fake_anyscale_client.py,sha256=Nn7NUvhzf-Hq3RZ4CCTfviwrCF5eD9PijuSw-kmSjMY,53180
|
37
37
|
anyscale/_private/docgen/README.md,sha256=z0tj8Jy0KmxWJBQMHKyzXGX_cYYgI8m5DCD6KCMU8oI,762
|
38
|
-
anyscale/_private/docgen/__main__.py,sha256=
|
38
|
+
anyscale/_private/docgen/__main__.py,sha256=ViMGLnYZT5VrPUhN9kHaHKBOoEsppu4Db7EPp4lG9zU,26158
|
39
39
|
anyscale/_private/docgen/api.md,sha256=SzLoDy6GyriU60l8964CNWz3z_QNEJA7mFHr0FzXyPU,32527
|
40
40
|
anyscale/_private/docgen/generator.py,sha256=dgC3qlqhJrTt0bTl3ExlRFLDwpn_Is2Yv47TJ33jaeE,21711
|
41
41
|
anyscale/_private/docgen/generator_legacy.py,sha256=pss_6ONF55XhARrKGcREDmg0J5plWact6USgb5Tr5mM,3002
|
@@ -774,18 +774,18 @@ anyscale/client/openapi_client/models/write_cluster_config.py,sha256=7Lvjku3OKT4
|
|
774
774
|
anyscale/client/openapi_client/models/write_project.py,sha256=48Jym8VDE57cZXTnVH_rl0bJlq7GPhLKKCje0G2P6yg,6686
|
775
775
|
anyscale/client/openapi_client/models/write_session.py,sha256=q1XvzytpzcDUpc29UVg9ebemwc_V4QrITCMcSRnfGHE,4154
|
776
776
|
anyscale/client/openapi_client/models/write_support_request.py,sha256=pFOalGDQi9FCDjeJV7ccBpq6iRq1Sub4NBfcUVfCglE,3614
|
777
|
-
anyscale/cloud/__init__.py,sha256=
|
778
|
-
anyscale/cloud/commands.py,sha256=
|
779
|
-
anyscale/cloud/models.py,sha256=
|
780
|
-
anyscale/cloud/_private/cloud_sdk.py,sha256=
|
777
|
+
anyscale/cloud/__init__.py,sha256=xkY385pSbFUdkp7OKzJUzpRDR22yXr1L3TukS2yzf44,3898
|
778
|
+
anyscale/cloud/commands.py,sha256=P9bB8LpEfhJVvfSdP7kijt0vokVpaYqDUr5T8X9OFJE,2830
|
779
|
+
anyscale/cloud/models.py,sha256=dG7Ilc3lXAGorAtzxgHiyBZ7dxngOBA35Mli22z-SNc,7155
|
780
|
+
anyscale/cloud/_private/cloud_sdk.py,sha256=HrsPQxlhxFU4Bliu4IX-tPKxCYYhIL39P1pajR3P_Iw,2795
|
781
781
|
anyscale/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
782
782
|
anyscale/commands/aggregated_instance_usage_commands.py,sha256=TRP1X3hdIWbKg9V20VtazlDXsYAeV--M0DH3-Z5tnj4,2293
|
783
783
|
anyscale/commands/auth_commands.py,sha256=X1g6Yu9kqgPb4HLODlZTYEk8G5AVLeyizPIgagWx-p0,1026
|
784
|
-
anyscale/commands/cloud_commands.py,sha256=
|
784
|
+
anyscale/commands/cloud_commands.py,sha256=C0AyWttfstcj66WX8y5hH4E0KMO4k33AQhnmGYzY5tw,41123
|
785
785
|
anyscale/commands/cloud_commands_util.py,sha256=d-6TSZ_syrGkZ3Fc1uRX6jG4dqYMebNkBNpYLojOJFg,247
|
786
786
|
anyscale/commands/cluster_commands.py,sha256=taNcffyFfqJ1MgOQd0cz9kzRXWFTdp-wfLPM4l_2tBc,13487
|
787
787
|
anyscale/commands/cluster_env_commands.py,sha256=KNWylyE8Ew1sDi7yu2Tp4RLcRu2_KJJJIzVGRyPflJo,3899
|
788
|
-
anyscale/commands/command_examples.py,sha256=
|
788
|
+
anyscale/commands/command_examples.py,sha256=Gpfy_hf-QzW9RZxgoAboa_yxm_MxzI5hXzV3fRuLMr0,22020
|
789
789
|
anyscale/commands/compute_config_commands.py,sha256=vdyrtMcdP8eeK32p_Y6zF-qps6_SyzprhbjRZ9p18tQ,7828
|
790
790
|
anyscale/commands/config_commands.py,sha256=p55uM6WrhfbFoRXC9hNAV-8c5ANghw7tBUYwaQDAtjE,7195
|
791
791
|
anyscale/commands/exec_commands.py,sha256=cMOP1u6xQbl81h69Jug3y73XnNSwpbM6XC1X57SIp4c,465
|
@@ -1160,10 +1160,10 @@ anyscale/workspace/__init__.py,sha256=fIxkn8b_HADCQl5njPAbcJxAf0sajZoc55L_wMvGAx
|
|
1160
1160
|
anyscale/workspace/commands.py,sha256=21FubFd2wmEwlVbk-ng-adwBm-O4ZPBgEJnoavbfvbU,14035
|
1161
1161
|
anyscale/workspace/models.py,sha256=Ey67KqxdslS51yK7xetbRaFjE8sURAArpf-F38r3cUU,9760
|
1162
1162
|
anyscale/workspace/_private/workspace_sdk.py,sha256=4LOBmMm7kd-O94ii5uP1UDbkWLComh6zI5QmE2lXRTY,26824
|
1163
|
-
anyscale-0.
|
1164
|
-
anyscale-0.
|
1165
|
-
anyscale-0.
|
1166
|
-
anyscale-0.
|
1167
|
-
anyscale-0.
|
1168
|
-
anyscale-0.
|
1169
|
-
anyscale-0.
|
1163
|
+
anyscale-0.26.0.dist-info/LICENSE,sha256=UOPu974Wzsna6frFv1mu4VrZgNdZT7lbcNPzo5ue3qs,3494
|
1164
|
+
anyscale-0.26.0.dist-info/METADATA,sha256=TkDYNHxtKchUqx_nuabG8G0lGLvGewXKU2q7ik4yL84,3049
|
1165
|
+
anyscale-0.26.0.dist-info/NOTICE,sha256=gHqDhSnUYlRXX-mDOL5FtE7774oiKyV_HO80qM3r9Xo,196
|
1166
|
+
anyscale-0.26.0.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
1167
|
+
anyscale-0.26.0.dist-info/entry_points.txt,sha256=NqO18sCZn6zG6J0S38itjcN00s7aE3C3v3k5lMAfCLk,51
|
1168
|
+
anyscale-0.26.0.dist-info/top_level.txt,sha256=g3NVNS8Oh0NZwbFFgeX696C5MZZkS5dqV2NqcsbDRJE,9
|
1169
|
+
anyscale-0.26.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|