anyscale 0.26.70__py3-none-any.whl → 0.26.71__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 +63 -6
- anyscale/_private/anyscale_client/common.py +33 -3
- anyscale/_private/anyscale_client/fake_anyscale_client.py +27 -2
- anyscale/client/README.md +29 -0
- anyscale/client/openapi_client/__init__.py +19 -0
- anyscale/client/openapi_client/api/default_api.py +1307 -4
- anyscale/client/openapi_client/models/__init__.py +19 -0
- anyscale/client/openapi_client/models/apply_multi_version_update_weights_update_model.py +152 -0
- anyscale/client/openapi_client/models/apply_version_weight_update_model.py +181 -0
- anyscale/client/openapi_client/models/backend_server_api_product_models_catalog_client_models_table_metadata.py +546 -0
- anyscale/client/openapi_client/models/backend_server_api_product_models_data_catalogs_table_metadata.py +178 -0
- anyscale/client/openapi_client/models/baseimagesenum.py +70 -1
- anyscale/client/openapi_client/models/catalog_metadata.py +150 -0
- anyscale/client/openapi_client/models/column_info.py +265 -0
- anyscale/client/openapi_client/models/compute_node_type.py +29 -1
- anyscale/client/openapi_client/models/connection_metadata.py +206 -0
- anyscale/client/openapi_client/models/create_workspace_template_version.py +31 -3
- anyscale/client/openapi_client/models/data_catalog.py +45 -31
- anyscale/client/openapi_client/models/data_catalog_connection.py +74 -58
- anyscale/client/openapi_client/models/data_catalog_object_type.py +100 -0
- anyscale/client/openapi_client/models/data_catalog_schema.py +324 -0
- anyscale/client/openapi_client/models/data_catalog_table.py +437 -0
- anyscale/client/openapi_client/models/data_catalog_volume.py +437 -0
- anyscale/client/openapi_client/models/datacatalogschema_list_response.py +147 -0
- anyscale/client/openapi_client/models/datacatalogtable_list_response.py +147 -0
- anyscale/client/openapi_client/models/datacatalogvolume_list_response.py +147 -0
- anyscale/client/openapi_client/models/decorated_serve_deployment.py +27 -1
- anyscale/client/openapi_client/models/decoratedproductionservicev2_versionapimodel_response.py +121 -0
- anyscale/client/openapi_client/models/describe_machine_pool_machines_filters.py +2 -2
- anyscale/client/openapi_client/models/describe_machine_pool_requests_filters.py +33 -5
- anyscale/client/openapi_client/models/describe_machine_pool_workloads_filters.py +2 -2
- anyscale/client/openapi_client/models/physical_resources.py +178 -0
- anyscale/client/openapi_client/models/schema_metadata.py +150 -0
- anyscale/client/openapi_client/models/sso_config.py +18 -18
- anyscale/client/openapi_client/models/supportedbaseimagesenum.py +70 -1
- anyscale/client/openapi_client/models/table_data_preview.py +209 -0
- anyscale/client/openapi_client/models/volume_metadata.py +150 -0
- anyscale/client/openapi_client/models/worker_node_type.py +29 -1
- anyscale/client/openapi_client/models/workspace_template_version.py +29 -1
- anyscale/client/openapi_client/models/workspace_template_version_data_object.py +29 -1
- anyscale/commands/job_commands.py +120 -0
- anyscale/commands/job_queue_commands.py +99 -2
- anyscale/commands/service_commands.py +139 -2
- anyscale/commands/util.py +104 -1
- anyscale/commands/workspace_commands.py +123 -5
- anyscale/commands/workspace_commands_v2.py +17 -1
- anyscale/compute_config/_private/compute_config_sdk.py +25 -12
- anyscale/compute_config/models.py +15 -0
- anyscale/controllers/job_controller.py +12 -0
- anyscale/controllers/workspace_controller.py +67 -5
- anyscale/job/_private/job_sdk.py +3 -1
- anyscale/job/models.py +16 -0
- anyscale/job_queue/__init__.py +37 -1
- anyscale/job_queue/_private/job_queue_sdk.py +28 -1
- anyscale/job_queue/commands.py +61 -1
- anyscale/sdk/anyscale_client/__init__.py +1 -0
- anyscale/sdk/anyscale_client/api/default_api.py +12 -2
- anyscale/sdk/anyscale_client/models/__init__.py +1 -0
- anyscale/sdk/anyscale_client/models/baseimagesenum.py +70 -1
- anyscale/sdk/anyscale_client/models/compute_node_type.py +29 -1
- anyscale/sdk/anyscale_client/models/physical_resources.py +178 -0
- anyscale/sdk/anyscale_client/models/supportedbaseimagesenum.py +70 -1
- anyscale/sdk/anyscale_client/models/worker_node_type.py +29 -1
- anyscale/service/__init__.py +40 -0
- anyscale/service/_private/service_sdk.py +121 -24
- anyscale/service/commands.py +75 -1
- anyscale/service/models.py +46 -2
- anyscale/shared_anyscale_utils/latest_ray_version.py +1 -1
- anyscale/version.py +1 -1
- anyscale/workspace/_private/workspace_sdk.py +1 -0
- anyscale/workspace/models.py +19 -0
- {anyscale-0.26.70.dist-info → anyscale-0.26.71.dist-info}/METADATA +1 -1
- {anyscale-0.26.70.dist-info → anyscale-0.26.71.dist-info}/RECORD +78 -58
- {anyscale-0.26.70.dist-info → anyscale-0.26.71.dist-info}/WHEEL +0 -0
- {anyscale-0.26.70.dist-info → anyscale-0.26.71.dist-info}/entry_points.txt +0 -0
- {anyscale-0.26.70.dist-info → anyscale-0.26.71.dist-info}/licenses/LICENSE +0 -0
- {anyscale-0.26.70.dist-info → anyscale-0.26.71.dist-info}/licenses/NOTICE +0 -0
- {anyscale-0.26.70.dist-info → anyscale-0.26.71.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,150 @@
|
|
|
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 VolumeMetadata(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
|
+
'owner': 'str',
|
|
37
|
+
'full_name': 'str'
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
attribute_map = {
|
|
41
|
+
'owner': 'owner',
|
|
42
|
+
'full_name': 'full_name'
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
def __init__(self, owner=None, full_name=None, local_vars_configuration=None): # noqa: E501
|
|
46
|
+
"""VolumeMetadata - a model defined in OpenAPI""" # noqa: E501
|
|
47
|
+
if local_vars_configuration is None:
|
|
48
|
+
local_vars_configuration = Configuration()
|
|
49
|
+
self.local_vars_configuration = local_vars_configuration
|
|
50
|
+
|
|
51
|
+
self._owner = None
|
|
52
|
+
self._full_name = None
|
|
53
|
+
self.discriminator = None
|
|
54
|
+
|
|
55
|
+
if owner is not None:
|
|
56
|
+
self.owner = owner
|
|
57
|
+
if full_name is not None:
|
|
58
|
+
self.full_name = full_name
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
def owner(self):
|
|
62
|
+
"""Gets the owner of this VolumeMetadata. # noqa: E501
|
|
63
|
+
|
|
64
|
+
Owner of the entity # noqa: E501
|
|
65
|
+
|
|
66
|
+
:return: The owner of this VolumeMetadata. # noqa: E501
|
|
67
|
+
:rtype: str
|
|
68
|
+
"""
|
|
69
|
+
return self._owner
|
|
70
|
+
|
|
71
|
+
@owner.setter
|
|
72
|
+
def owner(self, owner):
|
|
73
|
+
"""Sets the owner of this VolumeMetadata.
|
|
74
|
+
|
|
75
|
+
Owner of the entity # noqa: E501
|
|
76
|
+
|
|
77
|
+
:param owner: The owner of this VolumeMetadata. # noqa: E501
|
|
78
|
+
:type: str
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
self._owner = owner
|
|
82
|
+
|
|
83
|
+
@property
|
|
84
|
+
def full_name(self):
|
|
85
|
+
"""Gets the full_name of this VolumeMetadata. # noqa: E501
|
|
86
|
+
|
|
87
|
+
Fully qualified name of the entity # noqa: E501
|
|
88
|
+
|
|
89
|
+
:return: The full_name of this VolumeMetadata. # noqa: E501
|
|
90
|
+
:rtype: str
|
|
91
|
+
"""
|
|
92
|
+
return self._full_name
|
|
93
|
+
|
|
94
|
+
@full_name.setter
|
|
95
|
+
def full_name(self, full_name):
|
|
96
|
+
"""Sets the full_name of this VolumeMetadata.
|
|
97
|
+
|
|
98
|
+
Fully qualified name of the entity # noqa: E501
|
|
99
|
+
|
|
100
|
+
:param full_name: The full_name of this VolumeMetadata. # noqa: E501
|
|
101
|
+
:type: str
|
|
102
|
+
"""
|
|
103
|
+
|
|
104
|
+
self._full_name = full_name
|
|
105
|
+
|
|
106
|
+
def to_dict(self):
|
|
107
|
+
"""Returns the model properties as a dict"""
|
|
108
|
+
result = {}
|
|
109
|
+
|
|
110
|
+
for attr, _ in six.iteritems(self.openapi_types):
|
|
111
|
+
value = getattr(self, attr)
|
|
112
|
+
if isinstance(value, list):
|
|
113
|
+
result[attr] = list(map(
|
|
114
|
+
lambda x: x.to_dict() if hasattr(x, "to_dict") else x,
|
|
115
|
+
value
|
|
116
|
+
))
|
|
117
|
+
elif hasattr(value, "to_dict"):
|
|
118
|
+
result[attr] = value.to_dict()
|
|
119
|
+
elif isinstance(value, dict):
|
|
120
|
+
result[attr] = dict(map(
|
|
121
|
+
lambda item: (item[0], item[1].to_dict())
|
|
122
|
+
if hasattr(item[1], "to_dict") else item,
|
|
123
|
+
value.items()
|
|
124
|
+
))
|
|
125
|
+
else:
|
|
126
|
+
result[attr] = value
|
|
127
|
+
|
|
128
|
+
return result
|
|
129
|
+
|
|
130
|
+
def to_str(self):
|
|
131
|
+
"""Returns the string representation of the model"""
|
|
132
|
+
return pprint.pformat(self.to_dict())
|
|
133
|
+
|
|
134
|
+
def __repr__(self):
|
|
135
|
+
"""For `print` and `pprint`"""
|
|
136
|
+
return self.to_str()
|
|
137
|
+
|
|
138
|
+
def __eq__(self, other):
|
|
139
|
+
"""Returns true if both objects are equal"""
|
|
140
|
+
if not isinstance(other, VolumeMetadata):
|
|
141
|
+
return False
|
|
142
|
+
|
|
143
|
+
return self.to_dict() == other.to_dict()
|
|
144
|
+
|
|
145
|
+
def __ne__(self, other):
|
|
146
|
+
"""Returns true if both objects are not equal"""
|
|
147
|
+
if not isinstance(other, VolumeMetadata):
|
|
148
|
+
return True
|
|
149
|
+
|
|
150
|
+
return self.to_dict() != other.to_dict()
|
|
@@ -36,6 +36,7 @@ class WorkerNodeType(object):
|
|
|
36
36
|
'name': 'str',
|
|
37
37
|
'instance_type': 'str',
|
|
38
38
|
'resources': 'Resources',
|
|
39
|
+
'physical_resources': 'PhysicalResources',
|
|
39
40
|
'labels': 'dict(str, str)',
|
|
40
41
|
'aws_advanced_configurations_json': 'object',
|
|
41
42
|
'gcp_advanced_configurations_json': 'object',
|
|
@@ -51,6 +52,7 @@ class WorkerNodeType(object):
|
|
|
51
52
|
'name': 'name',
|
|
52
53
|
'instance_type': 'instance_type',
|
|
53
54
|
'resources': 'resources',
|
|
55
|
+
'physical_resources': 'physical_resources',
|
|
54
56
|
'labels': 'labels',
|
|
55
57
|
'aws_advanced_configurations_json': 'aws_advanced_configurations_json',
|
|
56
58
|
'gcp_advanced_configurations_json': 'gcp_advanced_configurations_json',
|
|
@@ -62,7 +64,7 @@ class WorkerNodeType(object):
|
|
|
62
64
|
'fallback_to_ondemand': 'fallback_to_ondemand'
|
|
63
65
|
}
|
|
64
66
|
|
|
65
|
-
def __init__(self, name=None, instance_type=None, resources=None, labels=None, aws_advanced_configurations_json=None, gcp_advanced_configurations_json=None, advanced_configurations_json=None, flags=None, min_workers=None, max_workers=None, use_spot=False, fallback_to_ondemand=False, local_vars_configuration=None): # noqa: E501
|
|
67
|
+
def __init__(self, name=None, instance_type=None, resources=None, physical_resources=None, labels=None, aws_advanced_configurations_json=None, gcp_advanced_configurations_json=None, advanced_configurations_json=None, flags=None, min_workers=None, max_workers=None, use_spot=False, fallback_to_ondemand=False, local_vars_configuration=None): # noqa: E501
|
|
66
68
|
"""WorkerNodeType - a model defined in OpenAPI""" # noqa: E501
|
|
67
69
|
if local_vars_configuration is None:
|
|
68
70
|
local_vars_configuration = Configuration()
|
|
@@ -71,6 +73,7 @@ class WorkerNodeType(object):
|
|
|
71
73
|
self._name = None
|
|
72
74
|
self._instance_type = None
|
|
73
75
|
self._resources = None
|
|
76
|
+
self._physical_resources = None
|
|
74
77
|
self._labels = None
|
|
75
78
|
self._aws_advanced_configurations_json = None
|
|
76
79
|
self._gcp_advanced_configurations_json = None
|
|
@@ -86,6 +89,8 @@ class WorkerNodeType(object):
|
|
|
86
89
|
self.instance_type = instance_type
|
|
87
90
|
if resources is not None:
|
|
88
91
|
self.resources = resources
|
|
92
|
+
if physical_resources is not None:
|
|
93
|
+
self.physical_resources = physical_resources
|
|
89
94
|
if labels is not None:
|
|
90
95
|
self.labels = labels
|
|
91
96
|
if aws_advanced_configurations_json is not None:
|
|
@@ -178,6 +183,29 @@ class WorkerNodeType(object):
|
|
|
178
183
|
|
|
179
184
|
self._resources = resources
|
|
180
185
|
|
|
186
|
+
@property
|
|
187
|
+
def physical_resources(self):
|
|
188
|
+
"""Gets the physical_resources of this WorkerNodeType. # noqa: E501
|
|
189
|
+
|
|
190
|
+
Physical resources for compute node type which specifies the actual CPU, memory, and GPU resources that should be allocated for this node type. # noqa: E501
|
|
191
|
+
|
|
192
|
+
:return: The physical_resources of this WorkerNodeType. # noqa: E501
|
|
193
|
+
:rtype: PhysicalResources
|
|
194
|
+
"""
|
|
195
|
+
return self._physical_resources
|
|
196
|
+
|
|
197
|
+
@physical_resources.setter
|
|
198
|
+
def physical_resources(self, physical_resources):
|
|
199
|
+
"""Sets the physical_resources of this WorkerNodeType.
|
|
200
|
+
|
|
201
|
+
Physical resources for compute node type which specifies the actual CPU, memory, and GPU resources that should be allocated for this node type. # noqa: E501
|
|
202
|
+
|
|
203
|
+
:param physical_resources: The physical_resources of this WorkerNodeType. # noqa: E501
|
|
204
|
+
:type: PhysicalResources
|
|
205
|
+
"""
|
|
206
|
+
|
|
207
|
+
self._physical_resources = physical_resources
|
|
208
|
+
|
|
181
209
|
@property
|
|
182
210
|
def labels(self):
|
|
183
211
|
"""Gets the labels of this WorkerNodeType. # noqa: E501
|
|
@@ -37,6 +37,7 @@ class WorkspaceTemplateVersion(object):
|
|
|
37
37
|
'image_uri': 'str',
|
|
38
38
|
'compute_configs': 'dict(str, str)',
|
|
39
39
|
'artifacts': 'WorkspaceSystemArtifacts',
|
|
40
|
+
'idle_termination_minutes': 'int',
|
|
40
41
|
'id': 'str',
|
|
41
42
|
'version': 'int',
|
|
42
43
|
'creator_id': 'str',
|
|
@@ -49,6 +50,7 @@ class WorkspaceTemplateVersion(object):
|
|
|
49
50
|
'image_uri': 'image_uri',
|
|
50
51
|
'compute_configs': 'compute_configs',
|
|
51
52
|
'artifacts': 'artifacts',
|
|
53
|
+
'idle_termination_minutes': 'idle_termination_minutes',
|
|
52
54
|
'id': 'id',
|
|
53
55
|
'version': 'version',
|
|
54
56
|
'creator_id': 'creator_id',
|
|
@@ -56,7 +58,7 @@ class WorkspaceTemplateVersion(object):
|
|
|
56
58
|
'created_at': 'created_at'
|
|
57
59
|
}
|
|
58
60
|
|
|
59
|
-
def __init__(self, template_id=None, image_uri=None, compute_configs=None, artifacts=None, id=None, version=None, creator_id=None, creator_email=None, created_at=None, local_vars_configuration=None): # noqa: E501
|
|
61
|
+
def __init__(self, template_id=None, image_uri=None, compute_configs=None, artifacts=None, idle_termination_minutes=None, id=None, version=None, creator_id=None, creator_email=None, created_at=None, local_vars_configuration=None): # noqa: E501
|
|
60
62
|
"""WorkspaceTemplateVersion - a model defined in OpenAPI""" # noqa: E501
|
|
61
63
|
if local_vars_configuration is None:
|
|
62
64
|
local_vars_configuration = Configuration()
|
|
@@ -66,6 +68,7 @@ class WorkspaceTemplateVersion(object):
|
|
|
66
68
|
self._image_uri = None
|
|
67
69
|
self._compute_configs = None
|
|
68
70
|
self._artifacts = None
|
|
71
|
+
self._idle_termination_minutes = None
|
|
69
72
|
self._id = None
|
|
70
73
|
self._version = None
|
|
71
74
|
self._creator_id = None
|
|
@@ -80,6 +83,8 @@ class WorkspaceTemplateVersion(object):
|
|
|
80
83
|
self.compute_configs = compute_configs
|
|
81
84
|
if artifacts is not None:
|
|
82
85
|
self.artifacts = artifacts
|
|
86
|
+
if idle_termination_minutes is not None:
|
|
87
|
+
self.idle_termination_minutes = idle_termination_minutes
|
|
83
88
|
self.id = id
|
|
84
89
|
self.version = version
|
|
85
90
|
self.creator_id = creator_id
|
|
@@ -180,6 +185,29 @@ class WorkspaceTemplateVersion(object):
|
|
|
180
185
|
|
|
181
186
|
self._artifacts = artifacts
|
|
182
187
|
|
|
188
|
+
@property
|
|
189
|
+
def idle_termination_minutes(self):
|
|
190
|
+
"""Gets the idle_termination_minutes of this WorkspaceTemplateVersion. # noqa: E501
|
|
191
|
+
|
|
192
|
+
Idle termination minutes for this version # noqa: E501
|
|
193
|
+
|
|
194
|
+
:return: The idle_termination_minutes of this WorkspaceTemplateVersion. # noqa: E501
|
|
195
|
+
:rtype: int
|
|
196
|
+
"""
|
|
197
|
+
return self._idle_termination_minutes
|
|
198
|
+
|
|
199
|
+
@idle_termination_minutes.setter
|
|
200
|
+
def idle_termination_minutes(self, idle_termination_minutes):
|
|
201
|
+
"""Sets the idle_termination_minutes of this WorkspaceTemplateVersion.
|
|
202
|
+
|
|
203
|
+
Idle termination minutes for this version # noqa: E501
|
|
204
|
+
|
|
205
|
+
:param idle_termination_minutes: The idle_termination_minutes of this WorkspaceTemplateVersion. # noqa: E501
|
|
206
|
+
:type: int
|
|
207
|
+
"""
|
|
208
|
+
|
|
209
|
+
self._idle_termination_minutes = idle_termination_minutes
|
|
210
|
+
|
|
183
211
|
@property
|
|
184
212
|
def id(self):
|
|
185
213
|
"""Gets the id of this WorkspaceTemplateVersion. # noqa: E501
|
|
@@ -37,6 +37,7 @@ class WorkspaceTemplateVersionDataObject(object):
|
|
|
37
37
|
'image_uri': 'str',
|
|
38
38
|
'compute_configs': 'dict(str, str)',
|
|
39
39
|
'artifacts': 'WorkspaceSystemArtifacts',
|
|
40
|
+
'idle_termination_minutes': 'int',
|
|
40
41
|
'id': 'str',
|
|
41
42
|
'version': 'int',
|
|
42
43
|
'creator_id': 'str',
|
|
@@ -49,6 +50,7 @@ class WorkspaceTemplateVersionDataObject(object):
|
|
|
49
50
|
'image_uri': 'image_uri',
|
|
50
51
|
'compute_configs': 'compute_configs',
|
|
51
52
|
'artifacts': 'artifacts',
|
|
53
|
+
'idle_termination_minutes': 'idle_termination_minutes',
|
|
52
54
|
'id': 'id',
|
|
53
55
|
'version': 'version',
|
|
54
56
|
'creator_id': 'creator_id',
|
|
@@ -56,7 +58,7 @@ class WorkspaceTemplateVersionDataObject(object):
|
|
|
56
58
|
'created_at': 'created_at'
|
|
57
59
|
}
|
|
58
60
|
|
|
59
|
-
def __init__(self, template_id=None, image_uri=None, compute_configs=None, artifacts=None, id=None, version=None, creator_id=None, creator_email=None, created_at=None, local_vars_configuration=None): # noqa: E501
|
|
61
|
+
def __init__(self, template_id=None, image_uri=None, compute_configs=None, artifacts=None, idle_termination_minutes=None, id=None, version=None, creator_id=None, creator_email=None, created_at=None, local_vars_configuration=None): # noqa: E501
|
|
60
62
|
"""WorkspaceTemplateVersionDataObject - a model defined in OpenAPI""" # noqa: E501
|
|
61
63
|
if local_vars_configuration is None:
|
|
62
64
|
local_vars_configuration = Configuration()
|
|
@@ -66,6 +68,7 @@ class WorkspaceTemplateVersionDataObject(object):
|
|
|
66
68
|
self._image_uri = None
|
|
67
69
|
self._compute_configs = None
|
|
68
70
|
self._artifacts = None
|
|
71
|
+
self._idle_termination_minutes = None
|
|
69
72
|
self._id = None
|
|
70
73
|
self._version = None
|
|
71
74
|
self._creator_id = None
|
|
@@ -80,6 +83,8 @@ class WorkspaceTemplateVersionDataObject(object):
|
|
|
80
83
|
self.compute_configs = compute_configs
|
|
81
84
|
if artifacts is not None:
|
|
82
85
|
self.artifacts = artifacts
|
|
86
|
+
if idle_termination_minutes is not None:
|
|
87
|
+
self.idle_termination_minutes = idle_termination_minutes
|
|
83
88
|
self.id = id
|
|
84
89
|
self.version = version
|
|
85
90
|
self.creator_id = creator_id
|
|
@@ -180,6 +185,29 @@ class WorkspaceTemplateVersionDataObject(object):
|
|
|
180
185
|
|
|
181
186
|
self._artifacts = artifacts
|
|
182
187
|
|
|
188
|
+
@property
|
|
189
|
+
def idle_termination_minutes(self):
|
|
190
|
+
"""Gets the idle_termination_minutes of this WorkspaceTemplateVersionDataObject. # noqa: E501
|
|
191
|
+
|
|
192
|
+
Idle termination minutes for this version # noqa: E501
|
|
193
|
+
|
|
194
|
+
:return: The idle_termination_minutes of this WorkspaceTemplateVersionDataObject. # noqa: E501
|
|
195
|
+
:rtype: int
|
|
196
|
+
"""
|
|
197
|
+
return self._idle_termination_minutes
|
|
198
|
+
|
|
199
|
+
@idle_termination_minutes.setter
|
|
200
|
+
def idle_termination_minutes(self, idle_termination_minutes):
|
|
201
|
+
"""Sets the idle_termination_minutes of this WorkspaceTemplateVersionDataObject.
|
|
202
|
+
|
|
203
|
+
Idle termination minutes for this version # noqa: E501
|
|
204
|
+
|
|
205
|
+
:param idle_termination_minutes: The idle_termination_minutes of this WorkspaceTemplateVersionDataObject. # noqa: E501
|
|
206
|
+
:type: int
|
|
207
|
+
"""
|
|
208
|
+
|
|
209
|
+
self._idle_termination_minutes = idle_termination_minutes
|
|
210
|
+
|
|
183
211
|
@property
|
|
184
212
|
def id(self):
|
|
185
213
|
"""Gets the id of this WorkspaceTemplateVersionDataObject. # noqa: E501
|
|
@@ -5,17 +5,31 @@ from subprocess import list2cmdline
|
|
|
5
5
|
from typing import List, Optional, Tuple
|
|
6
6
|
|
|
7
7
|
import click
|
|
8
|
+
from rich.console import Console
|
|
8
9
|
import yaml
|
|
9
10
|
|
|
10
11
|
import anyscale
|
|
11
12
|
from anyscale._private.models.image_uri import ImageURI
|
|
13
|
+
from anyscale.authenticate import get_auth_api_client
|
|
12
14
|
from anyscale.cli_logger import BlockLogger
|
|
15
|
+
from anyscale.client.openapi_client.models.delete_resource_tags_request import (
|
|
16
|
+
DeleteResourceTagsRequest,
|
|
17
|
+
)
|
|
13
18
|
from anyscale.client.openapi_client.models.ha_job_states import HaJobStates
|
|
19
|
+
from anyscale.client.openapi_client.models.resource_tag_resource_type import (
|
|
20
|
+
ResourceTagResourceType,
|
|
21
|
+
)
|
|
22
|
+
from anyscale.client.openapi_client.models.upsert_resource_tags_request import (
|
|
23
|
+
UpsertResourceTagsRequest,
|
|
24
|
+
)
|
|
14
25
|
from anyscale.commands import command_examples
|
|
15
26
|
from anyscale.commands.util import (
|
|
16
27
|
AnyscaleCommand,
|
|
28
|
+
build_kv_table,
|
|
17
29
|
convert_kv_strings_to_dict,
|
|
18
30
|
override_env_vars,
|
|
31
|
+
parse_repeatable_tags_to_dict,
|
|
32
|
+
parse_tags_kv_to_str_map,
|
|
19
33
|
)
|
|
20
34
|
from anyscale.controllers.job_controller import JobController
|
|
21
35
|
from anyscale.job.models import JobConfig, JobLogMode, JobState, JobStatus
|
|
@@ -129,6 +143,12 @@ def job_cli() -> None:
|
|
|
129
143
|
type=str,
|
|
130
144
|
help="Python modules to be available for import in the Ray workers. Each entry must be a path to a local directory.",
|
|
131
145
|
)
|
|
146
|
+
@click.option(
|
|
147
|
+
"--tag",
|
|
148
|
+
"tags",
|
|
149
|
+
multiple=True,
|
|
150
|
+
help="Tag in key=value (or key:value) format. Repeat to add multiple.",
|
|
151
|
+
)
|
|
132
152
|
@click.option(
|
|
133
153
|
"--cloud",
|
|
134
154
|
required=False,
|
|
@@ -182,6 +202,7 @@ def submit( # noqa: PLR0912 PLR0913 C901
|
|
|
182
202
|
exclude: Tuple[str],
|
|
183
203
|
requirements: Optional[str],
|
|
184
204
|
py_module: Tuple[str],
|
|
205
|
+
tags: Tuple[str],
|
|
185
206
|
cloud: Optional[str],
|
|
186
207
|
project: Optional[str],
|
|
187
208
|
max_retries: Optional[int],
|
|
@@ -336,6 +357,11 @@ and override the entrypoint with `python main.py`.
|
|
|
336
357
|
if timeout_s is not None:
|
|
337
358
|
config = config.options(timeout_s=timeout_s)
|
|
338
359
|
|
|
360
|
+
if tags:
|
|
361
|
+
tag_map = parse_tags_kv_to_str_map(tags)
|
|
362
|
+
if tag_map:
|
|
363
|
+
config = config.options(tags=tag_map)
|
|
364
|
+
|
|
339
365
|
log.info(f"Submitting job with config {config}.")
|
|
340
366
|
job_id = anyscale.job.submit(config)
|
|
341
367
|
|
|
@@ -379,6 +405,17 @@ and override the entrypoint with `python main.py`.
|
|
|
379
405
|
"If not provided, defaults to listing only unarchived jobs."
|
|
380
406
|
),
|
|
381
407
|
)
|
|
408
|
+
@click.option(
|
|
409
|
+
"--tag",
|
|
410
|
+
"tags",
|
|
411
|
+
multiple=True,
|
|
412
|
+
help=(
|
|
413
|
+
"This option can be repeated to filter by multiple tags. "
|
|
414
|
+
"Tags with the same key are ORed, whereas tags with different keys are ANDed. "
|
|
415
|
+
"Example: --tag team:mlops --tag team:infra --tag env:prod. "
|
|
416
|
+
"Filters with team: (mlops OR infra) AND env:prod."
|
|
417
|
+
),
|
|
418
|
+
)
|
|
382
419
|
@click.option(
|
|
383
420
|
"--max-items",
|
|
384
421
|
required=False,
|
|
@@ -404,6 +441,7 @@ def list( # noqa: A001 PLR0913
|
|
|
404
441
|
include_archived: bool,
|
|
405
442
|
max_items: int,
|
|
406
443
|
states: List[HaJobStates],
|
|
444
|
+
tags: List[str],
|
|
407
445
|
) -> None:
|
|
408
446
|
job_controller = JobController()
|
|
409
447
|
job_controller.list(
|
|
@@ -414,6 +452,7 @@ def list( # noqa: A001 PLR0913
|
|
|
414
452
|
include_archived=include_archived,
|
|
415
453
|
max_items=max_items,
|
|
416
454
|
states=states,
|
|
455
|
+
tags=parse_repeatable_tags_to_dict(tags) if tags else None,
|
|
417
456
|
)
|
|
418
457
|
|
|
419
458
|
|
|
@@ -755,3 +794,84 @@ status will be returned.
|
|
|
755
794
|
stream = StringIO()
|
|
756
795
|
yaml.dump(status_dict, stream, sort_keys=False)
|
|
757
796
|
print(stream.getvalue(), end="")
|
|
797
|
+
|
|
798
|
+
|
|
799
|
+
@job_cli.group("tags", help="Manage tags for jobs.")
|
|
800
|
+
def job_tags_cli() -> None:
|
|
801
|
+
pass
|
|
802
|
+
|
|
803
|
+
|
|
804
|
+
@job_tags_cli.command(name="add", help="Add or update tags on a job.")
|
|
805
|
+
@click.option("--id", "job_id", required=False, help="Unique ID of the job.")
|
|
806
|
+
@click.option("--name", "-n", required=False, help="Name of the job.")
|
|
807
|
+
@click.option(
|
|
808
|
+
"--tag",
|
|
809
|
+
"tags",
|
|
810
|
+
multiple=True,
|
|
811
|
+
help="Tag in key=value (or key:value) format. Repeat to add multiple.",
|
|
812
|
+
)
|
|
813
|
+
def job_tags_add(job_id: Optional[str], name: Optional[str], tags: Tuple[str]) -> None:
|
|
814
|
+
if not job_id and not name:
|
|
815
|
+
raise click.ClickException("Provide either --id or --name.")
|
|
816
|
+
tag_map = parse_tags_kv_to_str_map(tags)
|
|
817
|
+
if not tag_map:
|
|
818
|
+
raise click.ClickException("Provide at least one --tag key=value.")
|
|
819
|
+
req = UpsertResourceTagsRequest(
|
|
820
|
+
resource_type=ResourceTagResourceType.JOB,
|
|
821
|
+
resource_id=job_id or JobController().resolve_job_id(job_id, name),
|
|
822
|
+
tags=tag_map,
|
|
823
|
+
)
|
|
824
|
+
JobController().api_client.upsert_resource_tags_api_v2_tags_resource_put(req)
|
|
825
|
+
stderr = Console(stderr=True)
|
|
826
|
+
ident = job_id or name or "<unknown>"
|
|
827
|
+
stderr.print(f"Tags updated for job '{ident}'.")
|
|
828
|
+
|
|
829
|
+
|
|
830
|
+
@job_tags_cli.command(name="remove", help="Remove tags by key from a job.")
|
|
831
|
+
@click.option("--id", "job_id", required=False, help="Unique ID of the job.")
|
|
832
|
+
@click.option("--name", "-n", required=False, help="Name of the job.")
|
|
833
|
+
@click.option("--key", "keys", multiple=True, help="Tag key to remove. Repeatable.")
|
|
834
|
+
def job_tags_remove(
|
|
835
|
+
job_id: Optional[str], name: Optional[str], keys: Tuple[str]
|
|
836
|
+
) -> None:
|
|
837
|
+
if not job_id and not name:
|
|
838
|
+
raise click.ClickException("Provide either --id or --name.")
|
|
839
|
+
key_list = [k for k in keys if k and k.strip()]
|
|
840
|
+
if not key_list:
|
|
841
|
+
raise click.ClickException("Provide at least one --key to remove.")
|
|
842
|
+
req = DeleteResourceTagsRequest(
|
|
843
|
+
resource_type=ResourceTagResourceType.JOB,
|
|
844
|
+
resource_id=job_id or JobController().resolve_job_id(job_id, name),
|
|
845
|
+
keys=key_list,
|
|
846
|
+
)
|
|
847
|
+
JobController().api_client.delete_resource_tags_api_v2_tags_resource_delete(req)
|
|
848
|
+
stderr = Console(stderr=True)
|
|
849
|
+
ident = job_id or name or "<unknown>"
|
|
850
|
+
stderr.print(f"Removed tag keys {key_list} from job '{ident}'.")
|
|
851
|
+
|
|
852
|
+
|
|
853
|
+
@job_tags_cli.command(name="list", help="List tags for a job.")
|
|
854
|
+
@click.option("--id", "job_id", required=False, help="Unique ID of the job.")
|
|
855
|
+
@click.option("--name", "-n", required=False, help="Name of the job.")
|
|
856
|
+
@click.option("--json", "json_output", is_flag=True, default=False)
|
|
857
|
+
def job_tags_list(
|
|
858
|
+
job_id: Optional[str], name: Optional[str], json_output: bool
|
|
859
|
+
) -> None:
|
|
860
|
+
if not job_id and not name:
|
|
861
|
+
raise click.ClickException("Provide either --id or --name.")
|
|
862
|
+
if not job_id:
|
|
863
|
+
job_id = JobController().resolve_job_id(job_id, name)
|
|
864
|
+
auth = get_auth_api_client()
|
|
865
|
+
resp = auth.api_client.get_tags_for_resource_api_v2_tags_resource_get(
|
|
866
|
+
ResourceTagResourceType.JOB, job_id
|
|
867
|
+
)
|
|
868
|
+
tags = getattr(resp.result, "tags", [])
|
|
869
|
+
if json_output:
|
|
870
|
+
Console().print_json(json=json_dumps([t.to_dict() for t in tags], indent=2))
|
|
871
|
+
else:
|
|
872
|
+
stderr = Console(stderr=True)
|
|
873
|
+
if not tags:
|
|
874
|
+
stderr.print("No tags found.")
|
|
875
|
+
return
|
|
876
|
+
pairs = [(t.key, t.value) for t in tags]
|
|
877
|
+
stderr.print(build_kv_table(pairs, title="Tags"))
|
|
@@ -5,16 +5,20 @@ from enum import Enum
|
|
|
5
5
|
from functools import partial
|
|
6
6
|
from json import dumps as json_dumps
|
|
7
7
|
import sys
|
|
8
|
-
from typing import Dict, get_type_hints, List, Optional
|
|
8
|
+
from typing import Dict, get_type_hints, List, Optional, Tuple
|
|
9
9
|
|
|
10
10
|
import click
|
|
11
11
|
from rich.console import Console
|
|
12
12
|
from rich.table import Table
|
|
13
13
|
|
|
14
|
+
from anyscale.authenticate import get_auth_api_client
|
|
14
15
|
from anyscale.client.openapi_client.models.job_queue_sort_directive import (
|
|
15
16
|
JobQueueSortDirective,
|
|
16
17
|
)
|
|
17
18
|
from anyscale.client.openapi_client.models.job_queue_sort_field import JobQueueSortField
|
|
19
|
+
from anyscale.client.openapi_client.models.resource_tag_resource_type import (
|
|
20
|
+
ResourceTagResourceType,
|
|
21
|
+
)
|
|
18
22
|
from anyscale.client.openapi_client.models.session_state import SessionState
|
|
19
23
|
from anyscale.client.openapi_client.models.sort_order import SortOrder
|
|
20
24
|
from anyscale.commands import command_examples
|
|
@@ -24,7 +28,12 @@ from anyscale.commands.list_util import (
|
|
|
24
28
|
NON_INTERACTIVE_DEFAULT_MAX_ITEMS,
|
|
25
29
|
validate_page_size,
|
|
26
30
|
)
|
|
27
|
-
from anyscale.commands.util import
|
|
31
|
+
from anyscale.commands.util import (
|
|
32
|
+
AnyscaleCommand,
|
|
33
|
+
build_kv_table,
|
|
34
|
+
parse_repeatable_tags_to_dict,
|
|
35
|
+
parse_tags_kv_to_str_map,
|
|
36
|
+
)
|
|
28
37
|
import anyscale.job_queue
|
|
29
38
|
from anyscale.job_queue.models import JobQueueStatus, JobQueueStatusKeys
|
|
30
39
|
from anyscale.util import get_endpoint, get_user_info, validate_non_negative_arg
|
|
@@ -94,6 +103,17 @@ VIEW_COLUMNS: Dict[ViewOption, List[JobQueueStatusKeys]] = {
|
|
|
94
103
|
type=click.Choice(SessionState.allowable_values, case_sensitive=False),
|
|
95
104
|
help="Filter by cluster status.",
|
|
96
105
|
)
|
|
106
|
+
@click.option(
|
|
107
|
+
"--tag",
|
|
108
|
+
"tags",
|
|
109
|
+
multiple=True,
|
|
110
|
+
help=(
|
|
111
|
+
"This option can be repeated to filter by multiple tags. "
|
|
112
|
+
"Tags with the same key are ORed, whereas tags with different keys are ANDed. "
|
|
113
|
+
"Example: --tag team:mlops --tag team:infra --tag env:prod. "
|
|
114
|
+
"Filters with team: (mlops OR infra) AND env:prod."
|
|
115
|
+
),
|
|
116
|
+
)
|
|
97
117
|
@click.option(
|
|
98
118
|
"--view",
|
|
99
119
|
type=click.Choice([opt.value for opt in ViewOption], case_sensitive=False),
|
|
@@ -133,6 +153,7 @@ def list_job_queues( # noqa: PLR0913
|
|
|
133
153
|
cloud: Optional[str],
|
|
134
154
|
project: Optional[str],
|
|
135
155
|
cluster_status: Optional[str],
|
|
156
|
+
tags: List[str],
|
|
136
157
|
include_all_users: bool,
|
|
137
158
|
view: ViewOption,
|
|
138
159
|
page_size: int,
|
|
@@ -172,6 +193,7 @@ def list_job_queues( # noqa: PLR0913
|
|
|
172
193
|
creator_id=None if include_all_users else (user.id if user else None),
|
|
173
194
|
cloud=cloud,
|
|
174
195
|
project=project,
|
|
196
|
+
tags_filter=parse_repeatable_tags_to_dict(tags) if tags else None,
|
|
175
197
|
page_size=page_size,
|
|
176
198
|
max_items=None if not no_interactive else effective_max,
|
|
177
199
|
sorting_directives=sort_dirs,
|
|
@@ -240,6 +262,81 @@ def update_job_queue(
|
|
|
240
262
|
sys.exit(1)
|
|
241
263
|
|
|
242
264
|
|
|
265
|
+
@job_queue_cli.group("tags", help="Manage tags for job queues.")
|
|
266
|
+
def job_queue_tags_cli() -> None:
|
|
267
|
+
pass
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
@job_queue_tags_cli.command(name="add", help="Add or update tags on a job queue.")
|
|
271
|
+
@click.option("--id", "job_queue_id", help="ID of a job queue.")
|
|
272
|
+
@click.option("--name", type=str, help="Name of a job queue.")
|
|
273
|
+
@click.option(
|
|
274
|
+
"--tag",
|
|
275
|
+
"tags",
|
|
276
|
+
multiple=True,
|
|
277
|
+
help="Tag in key=value (or key:value) format. Repeat to add multiple.",
|
|
278
|
+
)
|
|
279
|
+
def job_queue_tags_add(
|
|
280
|
+
job_queue_id: Optional[str], name: Optional[str], tags: Tuple[str],
|
|
281
|
+
) -> None:
|
|
282
|
+
if not job_queue_id and not name:
|
|
283
|
+
raise click.ClickException("Provide either --id or --name.")
|
|
284
|
+
tag_map = parse_tags_kv_to_str_map(tags)
|
|
285
|
+
if not tag_map:
|
|
286
|
+
raise click.ClickException("Provide at least one --tag key=value.")
|
|
287
|
+
anyscale.job_queue.add_tags(job_queue_id=job_queue_id, name=name, tags=tag_map)
|
|
288
|
+
stderr = Console(stderr=True)
|
|
289
|
+
ident = job_queue_id or name or "<unknown>"
|
|
290
|
+
stderr.print(f"Tags updated for job queue '{ident}'.")
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
@job_queue_tags_cli.command(name="remove", help="Remove tags by key from a job queue.")
|
|
294
|
+
@click.option("--id", "job_queue_id", help="ID of a job queue.")
|
|
295
|
+
@click.option("--name", type=str, help="Name of a job queue.")
|
|
296
|
+
@click.option("--key", "keys", multiple=True, help="Tag key to remove. Repeatable.")
|
|
297
|
+
def job_queue_tags_remove(
|
|
298
|
+
job_queue_id: Optional[str], name: Optional[str], keys: Tuple[str],
|
|
299
|
+
) -> None:
|
|
300
|
+
if not job_queue_id and not name:
|
|
301
|
+
raise click.ClickException("Provide either --id or --name.")
|
|
302
|
+
key_list = [k for k in keys if k and k.strip()]
|
|
303
|
+
if not key_list:
|
|
304
|
+
raise click.ClickException("Provide at least one --key to remove.")
|
|
305
|
+
anyscale.job_queue.remove_tags(job_queue_id=job_queue_id, name=name, keys=key_list)
|
|
306
|
+
stderr = Console(stderr=True)
|
|
307
|
+
ident = job_queue_id or name or "<unknown>"
|
|
308
|
+
stderr.print(f"Removed tag keys {key_list} from job queue '{ident}'.")
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
@job_queue_tags_cli.command(name="list", help="List tags for a job queue.")
|
|
312
|
+
@click.option("--id", "job_queue_id", help="ID of a job queue.")
|
|
313
|
+
@click.option("--name", type=str, help="Name of a job queue.")
|
|
314
|
+
@click.option("--json", "json_output", is_flag=True, default=False)
|
|
315
|
+
def job_queue_tags_list(
|
|
316
|
+
job_queue_id: Optional[str], name: Optional[str], json_output: bool,
|
|
317
|
+
) -> None:
|
|
318
|
+
if not job_queue_id and not name:
|
|
319
|
+
raise click.ClickException("Provide either --id or --name.")
|
|
320
|
+
if not job_queue_id:
|
|
321
|
+
# Resolve ID via status (public SDK), which fetches by ID only; so instead list by name
|
|
322
|
+
jq = anyscale.job_queue.status(job_queue_id=anyscale.job_queue.list(name=name, max_items=1).__next__().id) # type: ignore
|
|
323
|
+
job_queue_id = jq.id
|
|
324
|
+
auth = get_auth_api_client()
|
|
325
|
+
resp = auth.api_client.get_tags_for_resource_api_v2_tags_resource_get(
|
|
326
|
+
ResourceTagResourceType.JOB_QUEUE, job_queue_id
|
|
327
|
+
)
|
|
328
|
+
tags = getattr(resp.result, "tags", [])
|
|
329
|
+
if json_output:
|
|
330
|
+
Console().print_json(json=json_dumps([t.to_dict() for t in tags], indent=2))
|
|
331
|
+
else:
|
|
332
|
+
stderr = Console(stderr=True)
|
|
333
|
+
if not tags:
|
|
334
|
+
stderr.print("No tags found.")
|
|
335
|
+
return
|
|
336
|
+
pairs = [(t.key, t.value) for t in tags]
|
|
337
|
+
stderr.print(build_kv_table(pairs, title="Tags"))
|
|
338
|
+
|
|
339
|
+
|
|
243
340
|
@job_queue_cli.command(
|
|
244
341
|
name="status",
|
|
245
342
|
help="Show job queue details.",
|