anyscale 0.26.19__py3-none-any.whl → 0.26.20__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/docgen/models.md +1 -1
- anyscale/client/README.md +6 -1
- anyscale/client/openapi_client/__init__.py +3 -0
- anyscale/client/openapi_client/api/default_api.py +269 -17
- anyscale/client/openapi_client/models/__init__.py +3 -0
- anyscale/client/openapi_client/models/decorated_production_job_state_transition.py +2 -2
- anyscale/client/openapi_client/models/job_queue_sort_directive.py +148 -0
- anyscale/client/openapi_client/models/job_queue_sort_field.py +107 -0
- anyscale/client/openapi_client/models/job_queues_query.py +31 -3
- anyscale/client/openapi_client/models/production_job_state_transition.py +2 -2
- anyscale/client/openapi_client/models/update_job_queue_request.py +150 -0
- anyscale/commands/command_examples.py +58 -0
- anyscale/commands/job_commands.py +2 -2
- anyscale/commands/job_queue_commands.py +172 -0
- anyscale/controllers/job_controller.py +215 -3
- anyscale/scripts.py +3 -0
- anyscale/sdk/anyscale_client/models/production_job_state_transition.py +2 -2
- anyscale/util.py +3 -1
- anyscale/utils/connect_helpers.py +34 -0
- anyscale/version.py +1 -1
- anyscale/workspace/_private/workspace_sdk.py +19 -6
- {anyscale-0.26.19.dist-info → anyscale-0.26.20.dist-info}/METADATA +1 -1
- {anyscale-0.26.19.dist-info → anyscale-0.26.20.dist-info}/RECORD +28 -24
- {anyscale-0.26.19.dist-info → anyscale-0.26.20.dist-info}/LICENSE +0 -0
- {anyscale-0.26.19.dist-info → anyscale-0.26.20.dist-info}/NOTICE +0 -0
- {anyscale-0.26.19.dist-info → anyscale-0.26.20.dist-info}/WHEEL +0 -0
- {anyscale-0.26.19.dist-info → anyscale-0.26.20.dist-info}/entry_points.txt +0 -0
- {anyscale-0.26.19.dist-info → anyscale-0.26.20.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,107 @@
|
|
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 JobQueueSortField(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
|
+
allowed enum values
|
30
|
+
"""
|
31
|
+
ID = "ID"
|
32
|
+
NAME = "NAME"
|
33
|
+
CREATED_AT = "CREATED_AT"
|
34
|
+
CREATOR_ID = "CREATOR_ID"
|
35
|
+
CREATOR_EMAIL = "CREATOR_EMAIL"
|
36
|
+
PROJECT_ID = "PROJECT_ID"
|
37
|
+
CLOUD_ID = "CLOUD_ID"
|
38
|
+
QUEUE_STATE = "QUEUE_STATE"
|
39
|
+
CLUSTER_STATE = "CLUSTER_STATE"
|
40
|
+
|
41
|
+
allowable_values = [ID, NAME, CREATED_AT, CREATOR_ID, CREATOR_EMAIL, PROJECT_ID, CLOUD_ID, QUEUE_STATE, CLUSTER_STATE] # noqa: E501
|
42
|
+
|
43
|
+
"""
|
44
|
+
Attributes:
|
45
|
+
openapi_types (dict): The key is attribute name
|
46
|
+
and the value is attribute type.
|
47
|
+
attribute_map (dict): The key is attribute name
|
48
|
+
and the value is json key in definition.
|
49
|
+
"""
|
50
|
+
openapi_types = {
|
51
|
+
}
|
52
|
+
|
53
|
+
attribute_map = {
|
54
|
+
}
|
55
|
+
|
56
|
+
def __init__(self, local_vars_configuration=None): # noqa: E501
|
57
|
+
"""JobQueueSortField - a model defined in OpenAPI""" # noqa: E501
|
58
|
+
if local_vars_configuration is None:
|
59
|
+
local_vars_configuration = Configuration()
|
60
|
+
self.local_vars_configuration = local_vars_configuration
|
61
|
+
self.discriminator = None
|
62
|
+
|
63
|
+
def to_dict(self):
|
64
|
+
"""Returns the model properties as a dict"""
|
65
|
+
result = {}
|
66
|
+
|
67
|
+
for attr, _ in six.iteritems(self.openapi_types):
|
68
|
+
value = getattr(self, attr)
|
69
|
+
if isinstance(value, list):
|
70
|
+
result[attr] = list(map(
|
71
|
+
lambda x: x.to_dict() if hasattr(x, "to_dict") else x,
|
72
|
+
value
|
73
|
+
))
|
74
|
+
elif hasattr(value, "to_dict"):
|
75
|
+
result[attr] = value.to_dict()
|
76
|
+
elif isinstance(value, dict):
|
77
|
+
result[attr] = dict(map(
|
78
|
+
lambda item: (item[0], item[1].to_dict())
|
79
|
+
if hasattr(item[1], "to_dict") else item,
|
80
|
+
value.items()
|
81
|
+
))
|
82
|
+
else:
|
83
|
+
result[attr] = value
|
84
|
+
|
85
|
+
return result
|
86
|
+
|
87
|
+
def to_str(self):
|
88
|
+
"""Returns the string representation of the model"""
|
89
|
+
return pprint.pformat(self.to_dict())
|
90
|
+
|
91
|
+
def __repr__(self):
|
92
|
+
"""For `print` and `pprint`"""
|
93
|
+
return self.to_str()
|
94
|
+
|
95
|
+
def __eq__(self, other):
|
96
|
+
"""Returns true if both objects are equal"""
|
97
|
+
if not isinstance(other, JobQueueSortField):
|
98
|
+
return False
|
99
|
+
|
100
|
+
return self.to_dict() == other.to_dict()
|
101
|
+
|
102
|
+
def __ne__(self, other):
|
103
|
+
"""Returns true if both objects are not equal"""
|
104
|
+
if not isinstance(other, JobQueueSortField):
|
105
|
+
return True
|
106
|
+
|
107
|
+
return self.to_dict() != other.to_dict()
|
@@ -38,7 +38,8 @@ class JobQueuesQuery(object):
|
|
38
38
|
'cluster_status': 'SessionState',
|
39
39
|
'project_id': 'str',
|
40
40
|
'cloud_id': 'str',
|
41
|
-
'paging': 'PageQuery'
|
41
|
+
'paging': 'PageQuery',
|
42
|
+
'sorting_directives': 'list[JobQueueSortDirective]'
|
42
43
|
}
|
43
44
|
|
44
45
|
attribute_map = {
|
@@ -47,10 +48,11 @@ class JobQueuesQuery(object):
|
|
47
48
|
'cluster_status': 'cluster_status',
|
48
49
|
'project_id': 'project_id',
|
49
50
|
'cloud_id': 'cloud_id',
|
50
|
-
'paging': 'paging'
|
51
|
+
'paging': 'paging',
|
52
|
+
'sorting_directives': 'sorting_directives'
|
51
53
|
}
|
52
54
|
|
53
|
-
def __init__(self, name=None, creator_id=None, cluster_status=None, project_id=None, cloud_id=None, paging=None, local_vars_configuration=None): # noqa: E501
|
55
|
+
def __init__(self, name=None, creator_id=None, cluster_status=None, project_id=None, cloud_id=None, paging=None, sorting_directives=None, local_vars_configuration=None): # noqa: E501
|
54
56
|
"""JobQueuesQuery - a model defined in OpenAPI""" # noqa: E501
|
55
57
|
if local_vars_configuration is None:
|
56
58
|
local_vars_configuration = Configuration()
|
@@ -62,6 +64,7 @@ class JobQueuesQuery(object):
|
|
62
64
|
self._project_id = None
|
63
65
|
self._cloud_id = None
|
64
66
|
self._paging = None
|
67
|
+
self._sorting_directives = None
|
65
68
|
self.discriminator = None
|
66
69
|
|
67
70
|
if name is not None:
|
@@ -76,6 +79,8 @@ class JobQueuesQuery(object):
|
|
76
79
|
self.cloud_id = cloud_id
|
77
80
|
if paging is not None:
|
78
81
|
self.paging = paging
|
82
|
+
if sorting_directives is not None:
|
83
|
+
self.sorting_directives = sorting_directives
|
79
84
|
|
80
85
|
@property
|
81
86
|
def name(self):
|
@@ -215,6 +220,29 @@ class JobQueuesQuery(object):
|
|
215
220
|
|
216
221
|
self._paging = paging
|
217
222
|
|
223
|
+
@property
|
224
|
+
def sorting_directives(self):
|
225
|
+
"""Gets the sorting_directives of this JobQueuesQuery. # noqa: E501
|
226
|
+
|
227
|
+
List of sorting criteria (multi-field sort supported). # noqa: E501
|
228
|
+
|
229
|
+
:return: The sorting_directives of this JobQueuesQuery. # noqa: E501
|
230
|
+
:rtype: list[JobQueueSortDirective]
|
231
|
+
"""
|
232
|
+
return self._sorting_directives
|
233
|
+
|
234
|
+
@sorting_directives.setter
|
235
|
+
def sorting_directives(self, sorting_directives):
|
236
|
+
"""Sets the sorting_directives of this JobQueuesQuery.
|
237
|
+
|
238
|
+
List of sorting criteria (multi-field sort supported). # noqa: E501
|
239
|
+
|
240
|
+
:param sorting_directives: The sorting_directives of this JobQueuesQuery. # noqa: E501
|
241
|
+
:type: list[JobQueueSortDirective]
|
242
|
+
"""
|
243
|
+
|
244
|
+
self._sorting_directives = sorting_directives
|
245
|
+
|
218
246
|
def to_dict(self):
|
219
247
|
"""Returns the model properties as a dict"""
|
220
248
|
result = {}
|
@@ -108,7 +108,7 @@ class ProductionJobStateTransition(object):
|
|
108
108
|
def state_transitioned_at(self):
|
109
109
|
"""Gets the state_transitioned_at of this ProductionJobStateTransition. # noqa: E501
|
110
110
|
|
111
|
-
The last time the state of this job was updated
|
111
|
+
The last time the state of this job was updated # noqa: E501
|
112
112
|
|
113
113
|
:return: The state_transitioned_at of this ProductionJobStateTransition. # noqa: E501
|
114
114
|
:rtype: datetime
|
@@ -119,7 +119,7 @@ class ProductionJobStateTransition(object):
|
|
119
119
|
def state_transitioned_at(self, state_transitioned_at):
|
120
120
|
"""Sets the state_transitioned_at of this ProductionJobStateTransition.
|
121
121
|
|
122
|
-
The last time the state of this job was updated
|
122
|
+
The last time the state of this job was updated # noqa: E501
|
123
123
|
|
124
124
|
:param state_transitioned_at: The state_transitioned_at of this ProductionJobStateTransition. # noqa: E501
|
125
125
|
:type: datetime
|
@@ -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 UpdateJobQueueRequest(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
|
+
'idle_timeout_sec': 'int',
|
37
|
+
'max_concurrency': 'int'
|
38
|
+
}
|
39
|
+
|
40
|
+
attribute_map = {
|
41
|
+
'idle_timeout_sec': 'idle_timeout_sec',
|
42
|
+
'max_concurrency': 'max_concurrency'
|
43
|
+
}
|
44
|
+
|
45
|
+
def __init__(self, idle_timeout_sec=None, max_concurrency=None, local_vars_configuration=None): # noqa: E501
|
46
|
+
"""UpdateJobQueueRequest - 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._idle_timeout_sec = None
|
52
|
+
self._max_concurrency = None
|
53
|
+
self.discriminator = None
|
54
|
+
|
55
|
+
if idle_timeout_sec is not None:
|
56
|
+
self.idle_timeout_sec = idle_timeout_sec
|
57
|
+
if max_concurrency is not None:
|
58
|
+
self.max_concurrency = max_concurrency
|
59
|
+
|
60
|
+
@property
|
61
|
+
def idle_timeout_sec(self):
|
62
|
+
"""Gets the idle_timeout_sec of this UpdateJobQueueRequest. # noqa: E501
|
63
|
+
|
64
|
+
Max number of jobs to be run concurrently. # noqa: E501
|
65
|
+
|
66
|
+
:return: The idle_timeout_sec of this UpdateJobQueueRequest. # noqa: E501
|
67
|
+
:rtype: int
|
68
|
+
"""
|
69
|
+
return self._idle_timeout_sec
|
70
|
+
|
71
|
+
@idle_timeout_sec.setter
|
72
|
+
def idle_timeout_sec(self, idle_timeout_sec):
|
73
|
+
"""Sets the idle_timeout_sec of this UpdateJobQueueRequest.
|
74
|
+
|
75
|
+
Max number of jobs to be run concurrently. # noqa: E501
|
76
|
+
|
77
|
+
:param idle_timeout_sec: The idle_timeout_sec of this UpdateJobQueueRequest. # noqa: E501
|
78
|
+
:type: int
|
79
|
+
"""
|
80
|
+
|
81
|
+
self._idle_timeout_sec = idle_timeout_sec
|
82
|
+
|
83
|
+
@property
|
84
|
+
def max_concurrency(self):
|
85
|
+
"""Gets the max_concurrency of this UpdateJobQueueRequest. # noqa: E501
|
86
|
+
|
87
|
+
Max period of time queue will be accepting new jobs, before being sealed off and its associated cluster being shutdown # noqa: E501
|
88
|
+
|
89
|
+
:return: The max_concurrency of this UpdateJobQueueRequest. # noqa: E501
|
90
|
+
:rtype: int
|
91
|
+
"""
|
92
|
+
return self._max_concurrency
|
93
|
+
|
94
|
+
@max_concurrency.setter
|
95
|
+
def max_concurrency(self, max_concurrency):
|
96
|
+
"""Sets the max_concurrency of this UpdateJobQueueRequest.
|
97
|
+
|
98
|
+
Max period of time queue will be accepting new jobs, before being sealed off and its associated cluster being shutdown # noqa: E501
|
99
|
+
|
100
|
+
:param max_concurrency: The max_concurrency of this UpdateJobQueueRequest. # noqa: E501
|
101
|
+
:type: int
|
102
|
+
"""
|
103
|
+
|
104
|
+
self._max_concurrency = max_concurrency
|
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, UpdateJobQueueRequest):
|
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, UpdateJobQueueRequest):
|
148
|
+
return True
|
149
|
+
|
150
|
+
return self.to_dict() != other.to_dict()
|
@@ -77,6 +77,64 @@ NAME ID COST PROJECT NAME CLUSTER NAME
|
|
77
77
|
my-job prodjob_s9x4uzc5jnkt5z53g4tujb3y2e 0 default cluster_for_prodjob_s9x4uzc5jnkt5z53g4tujb3y2e SUCCESS doc@anyscale.com python main.py
|
78
78
|
"""
|
79
79
|
|
80
|
+
JOB_QUEUE_LIST = """\
|
81
|
+
$ anyscale job-queue list
|
82
|
+
Output
|
83
|
+
JOB QUEUES:
|
84
|
+
ID NAME CLUSTER ID CREATOR ID MAX CONCURRENCY IDLE TIMEOUT SEC CURRENT CLUSTER STATE
|
85
|
+
jq_h8fcze2qkr8wttuuvapi1hvyuc queue_3 ses_cjr7uaf1yh2ue5uzvd11p24p4u usr_we8x7d7u8hq8mj2488ed9x47n6 3 5000 Terminated
|
86
|
+
jq_v5bx9z1sd4pbxasxhdms37j4gi queue_2 ses_k86raeu6k1t6z1bvyejn3vblad usr_we8x7d7u8hq8mj2488ed9x47n6 10 5000 Terminated
|
87
|
+
jq_ni6hk66nt3194msr7hzzj9daun queue_1 ses_uhb8a9gamtarz68kcurpjh86sa usr_we8x7d7u8hq8mj2488ed9x47n6 10 5000 Terminated
|
88
|
+
"""
|
89
|
+
|
90
|
+
JOB_QUEUE_INFO = """\
|
91
|
+
$ anyscale job-queue info --id jq_h8fcze2qkr8wttuuvapi1hvyuc
|
92
|
+
Output
|
93
|
+
ID : jq_h8fcze2qkr8wttuuvapi1hvyuc
|
94
|
+
USER PROVIDED ID : queue_3
|
95
|
+
NAME : queue_3
|
96
|
+
CURRENT JOB QUEUE STATE : ACTIVE
|
97
|
+
EXECUTION MODE : PRIORITY
|
98
|
+
MAX CONCURRENCY : 3
|
99
|
+
IDLE TIMEOUT SEC : 5000
|
100
|
+
CREATED AT : 2025-04-15 20:40:44
|
101
|
+
CREATOR ID : usr_we8x7d7u8hq8mj2488ed9x47n6
|
102
|
+
CREATOR EMAIL : test@anyscale.com
|
103
|
+
COMPUTE CONFIG ID : cpt_8hzsv1t4jvb6kwjhfqbfjw5i6b
|
104
|
+
CURRENT CLUSTER STATE : Terminated
|
105
|
+
CLUSTER ID : ses_cjr7uaf1yh2ue5uzvd11p24p4u
|
106
|
+
PROJECT ID : prj_7FWKGPGPaD3Q5mvk9zK2viBD
|
107
|
+
CLOUD ID : cld_kvedZWag2qA8i5BjxUevf5i7
|
108
|
+
TOTAL JOBS : 6
|
109
|
+
SUCCESSFUL JOBS : 6
|
110
|
+
FAILED JOBS : 0
|
111
|
+
ACTIVE JOBS : 0
|
112
|
+
"""
|
113
|
+
|
114
|
+
JOB_QUEUE_UPDATE = """\
|
115
|
+
$ anyscale job-queue update --id jq_h8fcze2qkr8wttuuvapi1hvyuc --max-concurrency 5
|
116
|
+
Output
|
117
|
+
ID : jq_h8fcze2qkr8wttuuvapi1hvyuc
|
118
|
+
USER PROVIDED ID : queue_3
|
119
|
+
NAME : queue_3
|
120
|
+
CURRENT JOB QUEUE STATE : ACTIVE
|
121
|
+
EXECUTION MODE : PRIORITY
|
122
|
+
MAX CONCURRENCY : 5
|
123
|
+
IDLE TIMEOUT SEC : 5000
|
124
|
+
CREATED AT : 2025-04-15 20:40:44
|
125
|
+
CREATOR ID : usr_we8x7d7u8hq8mj2488ed9x47n6
|
126
|
+
CREATOR EMAIL : test@anyscale.com
|
127
|
+
COMPUTE CONFIG ID : cpt_8hzsv1t4jvb6kwjhfqbfjw5i6b
|
128
|
+
CURRENT CLUSTER STATE : Terminated
|
129
|
+
CLUSTER ID : ses_cjr7uaf1yh2ue5uzvd11p24p4u
|
130
|
+
PROJECT ID : prj_7FWKGPGPaD3Q5mvk9zK2viBD
|
131
|
+
CLOUD ID : cld_kvedZWag2qA8i5BjxUevf5i7
|
132
|
+
TOTAL JOBS : 6
|
133
|
+
SUCCESSFUL JOBS : 6
|
134
|
+
FAILED JOBS : 0
|
135
|
+
ACTIVE JOBS : 0
|
136
|
+
"""
|
137
|
+
|
80
138
|
SCHEDULE_APPLY_EXAMPLE = """\
|
81
139
|
$ anyscale schedule apply -n my-schedule -f my-schedule.yaml
|
82
140
|
(anyscale +0.5s) Applying schedule with config ScheduleConfig(job_config=JobConfig(name='my-schedule', image_uri=None, compute_config=None, env_vars=None, py_modules=None, cloud=None, project=None, ray_version=None, job_queue_config=None), cron_expression='0 0 * * * *', timezone='UTC').
|
@@ -396,14 +396,14 @@ and override the entrypoint with `python main.py`.
|
|
396
396
|
help=f"Filter jobs by state. Accepts one or more states. Allowed states: {', '.join(HaJobStates.allowable_values)}",
|
397
397
|
callback=validate_list_jobs_state_filter,
|
398
398
|
)
|
399
|
-
def list( # noqa: A001
|
399
|
+
def list( # noqa: A001 PLR0913
|
400
400
|
name: Optional[str],
|
401
401
|
id: Optional[str], # noqa: A002
|
402
402
|
project_id: Optional[str],
|
403
403
|
include_all_users: bool,
|
404
404
|
include_archived: bool,
|
405
405
|
max_items: int,
|
406
|
-
states: List[
|
406
|
+
states: List[HaJobStates],
|
407
407
|
) -> None:
|
408
408
|
job_controller = JobController()
|
409
409
|
job_controller.list(
|
@@ -0,0 +1,172 @@
|
|
1
|
+
from typing import List
|
2
|
+
|
3
|
+
import click
|
4
|
+
|
5
|
+
from anyscale.client.openapi_client.models.job_queue_sort_directive import (
|
6
|
+
JobQueueSortDirective,
|
7
|
+
)
|
8
|
+
from anyscale.client.openapi_client.models.job_queue_sort_field import JobQueueSortField
|
9
|
+
from anyscale.client.openapi_client.models.sort_order import SortOrder
|
10
|
+
from anyscale.commands import command_examples
|
11
|
+
from anyscale.commands.util import AnyscaleCommand
|
12
|
+
from anyscale.controllers.job_controller import JobController, JobQueueView
|
13
|
+
from anyscale.util import validate_non_negative_arg
|
14
|
+
|
15
|
+
|
16
|
+
@click.group(
|
17
|
+
"job-queues", help="Interact with production job queues running on Anyscale."
|
18
|
+
)
|
19
|
+
def job_queue_cli() -> None:
|
20
|
+
pass
|
21
|
+
|
22
|
+
|
23
|
+
def parse_sort_fields(
|
24
|
+
param: str, sort_fields: List[str],
|
25
|
+
) -> List[JobQueueSortDirective]:
|
26
|
+
sort_directives = []
|
27
|
+
|
28
|
+
for field_str in sort_fields:
|
29
|
+
descending = field_str.startswith("-")
|
30
|
+
raw_field = field_str.lstrip("-").upper()
|
31
|
+
|
32
|
+
if raw_field not in JobQueueSortField.allowable_values:
|
33
|
+
raise click.UsageError(
|
34
|
+
f"{param} must be one of {', '.join([v.lower() for v in JobQueueSortField.allowable_values])}"
|
35
|
+
)
|
36
|
+
|
37
|
+
sort_directives.append(
|
38
|
+
JobQueueSortDirective(
|
39
|
+
sort_field=raw_field,
|
40
|
+
sort_order=SortOrder.DESC if descending else SortOrder.ASC,
|
41
|
+
)
|
42
|
+
)
|
43
|
+
|
44
|
+
return sort_directives
|
45
|
+
|
46
|
+
|
47
|
+
@job_queue_cli.command(
|
48
|
+
name="list",
|
49
|
+
short_help="List job queues.",
|
50
|
+
cls=AnyscaleCommand,
|
51
|
+
example=command_examples.JOB_QUEUE_LIST,
|
52
|
+
)
|
53
|
+
@click.option(
|
54
|
+
"--include-all-users",
|
55
|
+
is_flag=True,
|
56
|
+
default=False,
|
57
|
+
help="Include job queues not created by current user.",
|
58
|
+
)
|
59
|
+
@click.option(
|
60
|
+
"--view",
|
61
|
+
type=click.Choice([v.name.lower() for v in JobQueueView], case_sensitive=False),
|
62
|
+
default=JobQueueView.DEFAULT.name,
|
63
|
+
help="Select which view to display.",
|
64
|
+
callback=lambda _, __, value: JobQueueView[value.upper()],
|
65
|
+
)
|
66
|
+
@click.option(
|
67
|
+
"--page",
|
68
|
+
default=100,
|
69
|
+
type=int,
|
70
|
+
help="Page size (default 100).",
|
71
|
+
callback=validate_non_negative_arg,
|
72
|
+
)
|
73
|
+
@click.option(
|
74
|
+
"--max-items",
|
75
|
+
required=False,
|
76
|
+
type=int,
|
77
|
+
help="Max items to show in list (only valid in interactive mode).",
|
78
|
+
callback=lambda ctx, param, value: validate_non_negative_arg(ctx, param, value)
|
79
|
+
if value
|
80
|
+
else None,
|
81
|
+
)
|
82
|
+
@click.option(
|
83
|
+
"--sort",
|
84
|
+
"sorting_directives",
|
85
|
+
multiple=True,
|
86
|
+
default=[JobQueueSortField.CREATED_AT],
|
87
|
+
help=f"""
|
88
|
+
Sort by column(s). Prefix column with - to sort in descending order.
|
89
|
+
Supported columns: {', '.join([v.lower() for v in JobQueueSortField.allowable_values])}.
|
90
|
+
""",
|
91
|
+
callback=lambda _, __, value: parse_sort_fields("sort", list(value)),
|
92
|
+
)
|
93
|
+
@click.option(
|
94
|
+
"--interactive/--no-interactive",
|
95
|
+
default=True,
|
96
|
+
help="--no-interactive disables the default interactive mode.",
|
97
|
+
)
|
98
|
+
def list_job_queues(
|
99
|
+
include_all_users: bool,
|
100
|
+
view: JobQueueView,
|
101
|
+
page: int,
|
102
|
+
max_items: int,
|
103
|
+
sorting_directives: List[JobQueueSortDirective],
|
104
|
+
interactive: bool,
|
105
|
+
):
|
106
|
+
if max_items is not None and interactive:
|
107
|
+
raise click.UsageError("--max-items can only be used in non interactive mode.")
|
108
|
+
job_controller = JobController()
|
109
|
+
job_controller.list_job_queues(
|
110
|
+
max_items=max_items,
|
111
|
+
page_size=page,
|
112
|
+
include_all_users=include_all_users,
|
113
|
+
view=view,
|
114
|
+
sorting_directives=sorting_directives,
|
115
|
+
interactive=interactive,
|
116
|
+
)
|
117
|
+
|
118
|
+
|
119
|
+
@job_queue_cli.command(
|
120
|
+
name="update",
|
121
|
+
short_help="Update job queue.",
|
122
|
+
cls=AnyscaleCommand,
|
123
|
+
example=command_examples.JOB_QUEUE_UPDATE,
|
124
|
+
)
|
125
|
+
@click.option(
|
126
|
+
"--id", "job_queue_id", required=False, default=None, help="ID of the job queue."
|
127
|
+
)
|
128
|
+
@click.option(
|
129
|
+
"--name",
|
130
|
+
"job_queue_name",
|
131
|
+
required=False,
|
132
|
+
default=None,
|
133
|
+
help="Name of the job queue.",
|
134
|
+
)
|
135
|
+
@click.option(
|
136
|
+
"--max-concurrency",
|
137
|
+
required=False,
|
138
|
+
default=None,
|
139
|
+
help="Maximum concurrency of the job queue",
|
140
|
+
)
|
141
|
+
@click.option(
|
142
|
+
"--idle-timeout-s",
|
143
|
+
required=False,
|
144
|
+
default=None,
|
145
|
+
help="Idle timeout of the job queue",
|
146
|
+
)
|
147
|
+
def update_job_queue(
|
148
|
+
job_queue_id: str, job_queue_name: str, max_concurrency: int, idle_timeout_s: int
|
149
|
+
):
|
150
|
+
if job_queue_id is None and job_queue_name is None:
|
151
|
+
raise click.ClickException("ID or name of job queue is required")
|
152
|
+
job_controller = JobController()
|
153
|
+
job_controller.update_job_queue(
|
154
|
+
job_queue_id=job_queue_id,
|
155
|
+
job_queue_name=job_queue_name,
|
156
|
+
max_concurrency=max_concurrency,
|
157
|
+
idle_timeout_s=idle_timeout_s,
|
158
|
+
)
|
159
|
+
|
160
|
+
|
161
|
+
@job_queue_cli.command(
|
162
|
+
name="info",
|
163
|
+
short_help="Info of a job queue.",
|
164
|
+
cls=AnyscaleCommand,
|
165
|
+
example=command_examples.JOB_QUEUE_INFO,
|
166
|
+
)
|
167
|
+
@click.option(
|
168
|
+
"--id", "job_queue_id", required=True, default=None, help="ID of the job."
|
169
|
+
)
|
170
|
+
def get_job_queue(job_queue_id: str):
|
171
|
+
job_controller = JobController()
|
172
|
+
job_controller.get_job_queue(job_queue_id=job_queue_id)
|