anyscale 0.26.29__py3-none-any.whl → 0.26.31__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- anyscale/__init__.py +10 -0
- anyscale/_private/anyscale_client/anyscale_client.py +76 -60
- anyscale/_private/anyscale_client/common.py +39 -1
- anyscale/_private/anyscale_client/fake_anyscale_client.py +11 -0
- anyscale/_private/docgen/__main__.py +4 -0
- anyscale/_private/docgen/models.md +2 -2
- anyscale/client/README.md +2 -0
- anyscale/client/openapi_client/__init__.py +1 -0
- anyscale/client/openapi_client/api/default_api.py +118 -0
- anyscale/client/openapi_client/models/__init__.py +1 -0
- anyscale/client/openapi_client/models/baseimagesenum.py +68 -1
- anyscale/client/openapi_client/models/get_or_create_build_from_image_uri_request.py +207 -0
- anyscale/client/openapi_client/models/supportedbaseimagesenum.py +68 -1
- anyscale/cluster_compute.py +3 -8
- anyscale/commands/command_examples.py +10 -0
- anyscale/commands/job_queue_commands.py +295 -104
- anyscale/commands/list_util.py +14 -1
- anyscale/commands/machine_pool_commands.py +14 -2
- anyscale/commands/service_commands.py +6 -12
- anyscale/commands/workspace_commands_v2.py +462 -25
- anyscale/controllers/compute_config_controller.py +3 -19
- anyscale/controllers/job_controller.py +5 -210
- anyscale/job_queue/__init__.py +89 -0
- anyscale/job_queue/_private/job_queue_sdk.py +158 -0
- anyscale/job_queue/commands.py +130 -0
- anyscale/job_queue/models.py +284 -0
- anyscale/scripts.py +1 -1
- anyscale/sdk/anyscale_client/models/baseimagesenum.py +68 -1
- anyscale/sdk/anyscale_client/models/supportedbaseimagesenum.py +68 -1
- anyscale/shared_anyscale_utils/latest_ray_version.py +1 -1
- anyscale/utils/ssh_websocket_proxy.py +178 -0
- anyscale/version.py +1 -1
- {anyscale-0.26.29.dist-info → anyscale-0.26.31.dist-info}/METADATA +3 -1
- {anyscale-0.26.29.dist-info → anyscale-0.26.31.dist-info}/RECORD +39 -33
- {anyscale-0.26.29.dist-info → anyscale-0.26.31.dist-info}/LICENSE +0 -0
- {anyscale-0.26.29.dist-info → anyscale-0.26.31.dist-info}/NOTICE +0 -0
- {anyscale-0.26.29.dist-info → anyscale-0.26.31.dist-info}/WHEEL +0 -0
- {anyscale-0.26.29.dist-info → anyscale-0.26.31.dist-info}/entry_points.txt +0 -0
- {anyscale-0.26.29.dist-info → anyscale-0.26.31.dist-info}/top_level.txt +0 -0
@@ -1,13 +1,11 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
3
|
import asyncio
|
4
|
-
from collections import defaultdict
|
5
|
-
from enum import Enum
|
6
4
|
import os
|
7
5
|
import random
|
8
6
|
import string
|
9
7
|
import time
|
10
|
-
from typing import Any,
|
8
|
+
from typing import Any, cast, Dict, List, Optional
|
11
9
|
|
12
10
|
import click
|
13
11
|
import tabulate
|
@@ -27,21 +25,10 @@ from anyscale.client.openapi_client.models.create_internal_production_job import
|
|
27
25
|
from anyscale.client.openapi_client.models.create_job_queue_config import (
|
28
26
|
CreateJobQueueConfig,
|
29
27
|
)
|
30
|
-
from anyscale.client.openapi_client.models.decorated_job_queue import DecoratedJobQueue
|
31
28
|
from anyscale.client.openapi_client.models.decorated_production_job import (
|
32
29
|
DecoratedProductionJob,
|
33
30
|
)
|
34
|
-
from anyscale.client.openapi_client.models.decoratedjobqueue_response import (
|
35
|
-
DecoratedjobqueueResponse,
|
36
|
-
)
|
37
31
|
from anyscale.client.openapi_client.models.ha_job_states import HaJobStates
|
38
|
-
from anyscale.client.openapi_client.models.job_queue_sort_directive import (
|
39
|
-
JobQueueSortDirective,
|
40
|
-
)
|
41
|
-
from anyscale.client.openapi_client.models.job_queues_query import JobQueuesQuery
|
42
|
-
from anyscale.client.openapi_client.models.update_job_queue_request import (
|
43
|
-
UpdateJobQueueRequest,
|
44
|
-
)
|
45
32
|
from anyscale.controllers.base_controller import BaseController
|
46
33
|
from anyscale.models.job_model import JobConfig
|
47
34
|
from anyscale.project_utils import infer_project_id
|
@@ -60,7 +47,7 @@ from anyscale.util import (
|
|
60
47
|
populate_unspecified_cluster_configs_from_current_workspace,
|
61
48
|
validate_job_config_dict,
|
62
49
|
)
|
63
|
-
from anyscale.utils.connect_helpers import
|
50
|
+
from anyscale.utils.connect_helpers import search_entities
|
64
51
|
from anyscale.utils.runtime_env import override_runtime_env_config
|
65
52
|
from anyscale.utils.workload_types import Workload
|
66
53
|
|
@@ -664,198 +651,6 @@ class JobController(BaseController):
|
|
664
651
|
)
|
665
652
|
return job_runs
|
666
653
|
|
667
|
-
def
|
668
|
-
self
|
669
|
-
|
670
|
-
job_queue_name: str,
|
671
|
-
max_concurrency: Optional[int] = None,
|
672
|
-
idle_timeout_s: Optional[int] = None,
|
673
|
-
):
|
674
|
-
job_queue: DecoratedJobQueue = _resolve_object(
|
675
|
-
fetch_by_id=cast(
|
676
|
-
Callable[[str], DecoratedjobqueueResponse],
|
677
|
-
self.api_client.get_job_queue_api_v2_job_queues_job_queue_id_get,
|
678
|
-
),
|
679
|
-
fetch_by_id_param=job_queue_id,
|
680
|
-
fetch_by_name=cast(
|
681
|
-
Callable[[str], DecoratedjobqueueResponse],
|
682
|
-
self.api_client.list_job_queues_api_v2_job_queues_post,
|
683
|
-
),
|
684
|
-
fetch_by_name_query={
|
685
|
-
"job_queues_query": {"name": {"equals": job_queue_name,}}
|
686
|
-
},
|
687
|
-
object_type_description="job queue",
|
688
|
-
)
|
689
|
-
|
690
|
-
queue: DecoratedJobQueue = self.api_client.update_job_queue_api_v2_job_queues_job_queue_id_put(
|
691
|
-
job_queue_id=job_queue.id,
|
692
|
-
update_job_queue_request=UpdateJobQueueRequest(
|
693
|
-
max_concurrency=max_concurrency, idle_timeout_sec=idle_timeout_s,
|
694
|
-
),
|
695
|
-
).result
|
696
|
-
|
697
|
-
_print_job_queue_vertical(queue, JobQueueView.ALL)
|
698
|
-
|
699
|
-
def get_job_queue(self, job_queue_id: str):
|
700
|
-
queue: DecoratedJobQueue = self.api_client.get_job_queue_api_v2_job_queues_job_queue_id_get(
|
701
|
-
job_queue_id=job_queue_id
|
702
|
-
).result
|
703
|
-
|
704
|
-
_print_job_queue_vertical(queue, JobQueueView.ALL)
|
705
|
-
|
706
|
-
def list_job_queues(
|
707
|
-
self,
|
708
|
-
include_all_users: bool,
|
709
|
-
view: JobQueueView,
|
710
|
-
page_size: int,
|
711
|
-
max_items: Optional[int],
|
712
|
-
sorting_directives: List[JobQueueSortDirective],
|
713
|
-
interactive: bool,
|
714
|
-
):
|
715
|
-
creator_id = (
|
716
|
-
None
|
717
|
-
if include_all_users
|
718
|
-
else self.api_client.get_user_info_api_v2_userinfo_get().result.id
|
719
|
-
)
|
720
|
-
|
721
|
-
def build_query(paging_token: Optional[str], count: int) -> Dict:
|
722
|
-
return {
|
723
|
-
"job_queues_query": JobQueuesQuery(
|
724
|
-
creator_id=creator_id,
|
725
|
-
paging=PageQuery(paging_token=paging_token, count=count),
|
726
|
-
sorting_directives=sorting_directives,
|
727
|
-
)
|
728
|
-
}
|
729
|
-
|
730
|
-
for batch in paginate(
|
731
|
-
search_function=self.api_client.list_job_queues_api_v2_job_queues_post,
|
732
|
-
query_builder=build_query,
|
733
|
-
interactive=interactive,
|
734
|
-
page_size=page_size,
|
735
|
-
max_items=max_items,
|
736
|
-
):
|
737
|
-
_render_job_queues(batch, view)
|
738
|
-
|
739
|
-
|
740
|
-
def _render_jobs(jobs):
|
741
|
-
jobs_table = [
|
742
|
-
[
|
743
|
-
job.name,
|
744
|
-
job.id,
|
745
|
-
job.project.name,
|
746
|
-
job.last_job_run.cluster.name
|
747
|
-
if job.last_job_run and job.last_job_run.cluster
|
748
|
-
else None,
|
749
|
-
job.state.current_state,
|
750
|
-
job.creator.email,
|
751
|
-
job.config.entrypoint
|
752
|
-
if len(job.config.entrypoint) < 50
|
753
|
-
else job.config.entrypoint[:50] + " ...",
|
754
|
-
]
|
755
|
-
for job in jobs
|
756
|
-
]
|
757
|
-
|
758
|
-
table = tabulate.tabulate(
|
759
|
-
jobs_table,
|
760
|
-
headers=[
|
761
|
-
"NAME",
|
762
|
-
"ID",
|
763
|
-
"PROJECT NAME",
|
764
|
-
"CLUSTER NAME",
|
765
|
-
"CURRENT STATE",
|
766
|
-
"CREATOR",
|
767
|
-
"ENTRYPOINT",
|
768
|
-
],
|
769
|
-
tablefmt="plain",
|
770
|
-
)
|
771
|
-
click.echo(f"{table}")
|
772
|
-
|
773
|
-
|
774
|
-
class JobQueueView(Enum):
|
775
|
-
ALL = DecoratedJobQueue.attribute_map.keys()
|
776
|
-
STATS = [
|
777
|
-
"id",
|
778
|
-
"name",
|
779
|
-
"total_jobs",
|
780
|
-
"active_jobs",
|
781
|
-
"successful_jobs",
|
782
|
-
"failed_jobs",
|
783
|
-
]
|
784
|
-
DEFAULT = [
|
785
|
-
"id",
|
786
|
-
"name",
|
787
|
-
"cluster_id",
|
788
|
-
"creator_id",
|
789
|
-
"max_concurrency",
|
790
|
-
"idle_timeout_sec",
|
791
|
-
"current_cluster_state",
|
792
|
-
"created_at",
|
793
|
-
]
|
794
|
-
|
795
|
-
|
796
|
-
def _format_job_queue(queue: DecoratedJobQueue, view: JobQueueView) -> List[str]:
|
797
|
-
formatters: Dict[str, Callable[[Any], Any]] = defaultdict(lambda: (lambda v: v))
|
798
|
-
formatters["created_at"] = lambda k: k.strftime("%Y-%m-%d %H:%M:%S")
|
799
|
-
|
800
|
-
return [formatters[field](getattr(queue, field, "")) or "" for field in view.value]
|
801
|
-
|
802
|
-
|
803
|
-
def _render_job_queues(queues: Iterable[DecoratedJobQueue], view: JobQueueView):
|
804
|
-
if not queues:
|
805
|
-
click.echo("No job queues found!")
|
806
|
-
return
|
807
|
-
table = tabulate.tabulate(
|
808
|
-
[
|
809
|
-
_format_job_queue(queue, view)
|
810
|
-
for queue in cast(Iterable[DecoratedJobQueue], queues)
|
811
|
-
],
|
812
|
-
headers=[field.replace("_", " ").upper() for field in view.value],
|
813
|
-
tablefmt="plain",
|
814
|
-
maxcolwidths=30, # type: ignore
|
815
|
-
numalign="center",
|
816
|
-
stralign="center",
|
817
|
-
)
|
818
|
-
click.echo(table)
|
819
|
-
|
820
|
-
|
821
|
-
def _print_job_queue_vertical(queue: DecoratedJobQueue, job_queue_view: JobQueueView):
|
822
|
-
"""
|
823
|
-
Print single job queue with headers as a vertical table
|
824
|
-
"""
|
825
|
-
for header, value in zip(
|
826
|
-
[field.replace("_", " ").upper() for field in job_queue_view.value],
|
827
|
-
_format_job_queue(queue, job_queue_view),
|
828
|
-
):
|
829
|
-
print(f"{header:<{30}}: {value}")
|
830
|
-
|
831
|
-
|
832
|
-
def _resolve_object(
|
833
|
-
fetch_by_id: Optional[Callable[[str], object]],
|
834
|
-
fetch_by_id_param: Optional[str],
|
835
|
-
fetch_by_name,
|
836
|
-
fetch_by_name_query,
|
837
|
-
object_type_description: str,
|
838
|
-
) -> Any:
|
839
|
-
"""Given job_id or job_name, retrieve decorated ha job spec"""
|
840
|
-
if fetch_by_id_param is None and fetch_by_name_query is None:
|
841
|
-
raise click.ClickException(
|
842
|
-
"Either `--id` or `--name` must be passed in for object."
|
843
|
-
)
|
844
|
-
if fetch_by_id_param:
|
845
|
-
try:
|
846
|
-
return fetch_by_id(fetch_by_id_param).result # type: ignore
|
847
|
-
except Exception as e: # noqa: BLE001
|
848
|
-
raise click.ClickException(
|
849
|
-
f"Could not fetch {object_type_description} by id: {e}"
|
850
|
-
)
|
851
|
-
|
852
|
-
object_list_resp: List[Any] = fetch_by_name(**fetch_by_name_query).results
|
853
|
-
if len(object_list_resp) == 0:
|
854
|
-
raise click.ClickException(
|
855
|
-
f"No {object_type_description} found with the provided name"
|
856
|
-
)
|
857
|
-
if len(object_list_resp) > 1:
|
858
|
-
raise click.ClickException(
|
859
|
-
f"Multiple {object_type_description}s found with the provided name"
|
860
|
-
)
|
861
|
-
return object_list_resp[0]
|
654
|
+
def get_authenticated_user_id(self) -> str:
|
655
|
+
user_info_response = self.api_client.get_user_info_api_v2_userinfo_get()
|
656
|
+
return user_info_response.result.id
|
@@ -0,0 +1,89 @@
|
|
1
|
+
from typing import List, Optional
|
2
|
+
|
3
|
+
from anyscale._private.anyscale_client import AnyscaleClient
|
4
|
+
from anyscale._private.models.model_base import ResultIterator
|
5
|
+
from anyscale._private.sdk import sdk_command, sdk_docs
|
6
|
+
from anyscale._private.sdk.base_sdk import Timer
|
7
|
+
from anyscale.cli_logger import BlockLogger
|
8
|
+
from anyscale.client.openapi_client.models.job_queue_sort_directive import (
|
9
|
+
JobQueueSortDirective,
|
10
|
+
)
|
11
|
+
from anyscale.client.openapi_client.models.session_state import SessionState
|
12
|
+
from anyscale.job_queue._private.job_queue_sdk import PrivateJobQueueSDK
|
13
|
+
from anyscale.job_queue.commands import (
|
14
|
+
_JOB_QUEUE_SDK_SINGLETON_KEY,
|
15
|
+
_LIST_ARG_DOCSTRINGS,
|
16
|
+
_LIST_EXAMPLE,
|
17
|
+
_STATUS_ARG_DOCSTRINGS,
|
18
|
+
_STATUS_EXAMPLE,
|
19
|
+
_UPDATE_ARG_DOCSTRINGS,
|
20
|
+
_UPDATE_EXAMPLE,
|
21
|
+
list,
|
22
|
+
status,
|
23
|
+
update,
|
24
|
+
)
|
25
|
+
from anyscale.job_queue.models import JobQueueSortField, JobQueueState, JobQueueStatus
|
26
|
+
|
27
|
+
|
28
|
+
class JobQueueSDK:
|
29
|
+
"""Public SDK for interacting with Anyscale Job Queues."""
|
30
|
+
|
31
|
+
def __init__(
|
32
|
+
self,
|
33
|
+
*,
|
34
|
+
client: Optional[AnyscaleClient] = None,
|
35
|
+
logger: Optional[BlockLogger] = None,
|
36
|
+
timer: Optional[Timer] = None,
|
37
|
+
):
|
38
|
+
self._private_sdk = PrivateJobQueueSDK(
|
39
|
+
client=client, logger=logger, timer=timer
|
40
|
+
)
|
41
|
+
|
42
|
+
@sdk_docs(doc_py_example=_LIST_EXAMPLE, arg_docstrings=_LIST_ARG_DOCSTRINGS)
|
43
|
+
def list( # noqa: F811
|
44
|
+
self,
|
45
|
+
*,
|
46
|
+
job_queue_id: Optional[str] = None,
|
47
|
+
name: Optional[str] = None,
|
48
|
+
creator_id: Optional[str] = None,
|
49
|
+
cloud: Optional[str] = None,
|
50
|
+
project: Optional[str] = None,
|
51
|
+
cluster_status: Optional[SessionState] = None,
|
52
|
+
page_size: Optional[int] = None,
|
53
|
+
max_items: Optional[int] = None,
|
54
|
+
sorting_directives: Optional[List[JobQueueSortDirective]] = None,
|
55
|
+
) -> ResultIterator[JobQueueStatus]:
|
56
|
+
"""List job queues or fetch a single job queue by ID."""
|
57
|
+
return self._private_sdk.list(
|
58
|
+
job_queue_id=job_queue_id,
|
59
|
+
name=name,
|
60
|
+
creator_id=creator_id,
|
61
|
+
cloud=cloud,
|
62
|
+
project=project,
|
63
|
+
cluster_status=cluster_status,
|
64
|
+
page_size=page_size,
|
65
|
+
max_items=max_items,
|
66
|
+
sorting_directives=sorting_directives,
|
67
|
+
)
|
68
|
+
|
69
|
+
@sdk_docs(doc_py_example=_STATUS_EXAMPLE, arg_docstrings=_STATUS_ARG_DOCSTRINGS)
|
70
|
+
def status(self, job_queue_id: str) -> JobQueueStatus: # noqa: F811
|
71
|
+
"""Get the status and details for a specific job queue."""
|
72
|
+
return self._private_sdk.status(job_queue_id=job_queue_id)
|
73
|
+
|
74
|
+
@sdk_docs(doc_py_example=_UPDATE_EXAMPLE, arg_docstrings=_UPDATE_ARG_DOCSTRINGS)
|
75
|
+
def update( # noqa: F811
|
76
|
+
self,
|
77
|
+
*,
|
78
|
+
job_queue_id: Optional[str] = None,
|
79
|
+
job_queue_name: Optional[str] = None,
|
80
|
+
max_concurrency: Optional[int] = None,
|
81
|
+
idle_timeout_s: Optional[int] = None,
|
82
|
+
) -> JobQueueStatus:
|
83
|
+
"""Update a job queue."""
|
84
|
+
return self._private_sdk.update(
|
85
|
+
job_queue_id=job_queue_id,
|
86
|
+
job_queue_name=job_queue_name,
|
87
|
+
max_concurrency=max_concurrency,
|
88
|
+
idle_timeout_s=idle_timeout_s,
|
89
|
+
)
|
@@ -0,0 +1,158 @@
|
|
1
|
+
from typing import List, Optional
|
2
|
+
|
3
|
+
from anyscale._private.models.model_base import ResultIterator
|
4
|
+
from anyscale._private.workload import WorkloadSDK
|
5
|
+
from anyscale.client.openapi_client.models.decorated_job_queue import DecoratedJobQueue
|
6
|
+
from anyscale.client.openapi_client.models.decoratedjobqueue_list_response import (
|
7
|
+
DecoratedjobqueueListResponse,
|
8
|
+
)
|
9
|
+
from anyscale.client.openapi_client.models.job_queue_sort_directive import (
|
10
|
+
JobQueueSortDirective,
|
11
|
+
)
|
12
|
+
from anyscale.client.openapi_client.models.list_response_metadata import (
|
13
|
+
ListResponseMetadata,
|
14
|
+
)
|
15
|
+
from anyscale.client.openapi_client.models.session_state import SessionState
|
16
|
+
from anyscale.job_queue.models import JobQueueStatus
|
17
|
+
|
18
|
+
|
19
|
+
class PrivateJobQueueSDK(WorkloadSDK):
|
20
|
+
"""Internal SDK logic for Job Queue operations."""
|
21
|
+
|
22
|
+
def list(
|
23
|
+
self,
|
24
|
+
*,
|
25
|
+
job_queue_id: Optional[str] = None,
|
26
|
+
name: Optional[str] = None,
|
27
|
+
creator_id: Optional[str] = None,
|
28
|
+
cloud: Optional[str] = None,
|
29
|
+
project: Optional[str] = None,
|
30
|
+
cluster_status: Optional[SessionState] = None,
|
31
|
+
page_size: Optional[int] = None,
|
32
|
+
max_items: Optional[int] = None,
|
33
|
+
sorting_directives: Optional[List[JobQueueSortDirective]] = None,
|
34
|
+
) -> ResultIterator[JobQueueStatus]:
|
35
|
+
"""List job queues based on specified filters and pagination.
|
36
|
+
|
37
|
+
If job_queue_id is provided, fetches only that specific job queue.
|
38
|
+
"""
|
39
|
+
|
40
|
+
if job_queue_id is not None:
|
41
|
+
raw = self._resolve_to_job_queue_model(job_queue_id=job_queue_id)
|
42
|
+
|
43
|
+
def _fetch_single_page(
|
44
|
+
_token: Optional[str],
|
45
|
+
) -> DecoratedjobqueueListResponse:
|
46
|
+
# Only return data on the first call (token=None), simulate single-item page
|
47
|
+
if _token is None and raw is not None:
|
48
|
+
results = [raw]
|
49
|
+
metadata = ListResponseMetadata(total=1, next_paging_token=None)
|
50
|
+
else:
|
51
|
+
results = []
|
52
|
+
metadata = ListResponseMetadata(total=0, next_paging_token=None)
|
53
|
+
|
54
|
+
return DecoratedjobqueueListResponse(
|
55
|
+
results=results, metadata=metadata,
|
56
|
+
)
|
57
|
+
|
58
|
+
return ResultIterator(
|
59
|
+
page_token=None,
|
60
|
+
max_items=0,
|
61
|
+
fetch_page=_fetch_single_page,
|
62
|
+
parse_fn=_parse_decorated_jq_to_status,
|
63
|
+
)
|
64
|
+
|
65
|
+
def _fetch_page(token: Optional[str]) -> DecoratedjobqueueListResponse:
|
66
|
+
return self.client.list_job_queues(
|
67
|
+
name=name,
|
68
|
+
creator_id=creator_id,
|
69
|
+
cloud=cloud,
|
70
|
+
project=project,
|
71
|
+
cluster_status=cluster_status,
|
72
|
+
count=page_size,
|
73
|
+
paging_token=token,
|
74
|
+
sorting_directives=sorting_directives,
|
75
|
+
)
|
76
|
+
|
77
|
+
return ResultIterator(
|
78
|
+
page_token=None,
|
79
|
+
max_items=max_items,
|
80
|
+
fetch_page=_fetch_page,
|
81
|
+
parse_fn=_parse_decorated_jq_to_status,
|
82
|
+
)
|
83
|
+
|
84
|
+
def status(self, job_queue_id: str) -> JobQueueStatus:
|
85
|
+
"""Get the status and details for a specific job queue."""
|
86
|
+
raw = self._resolve_to_job_queue_model(job_queue_id=job_queue_id)
|
87
|
+
return _parse_decorated_jq_to_status(raw)
|
88
|
+
|
89
|
+
def update(
|
90
|
+
self,
|
91
|
+
*,
|
92
|
+
job_queue_id: Optional[str] = None,
|
93
|
+
job_queue_name: Optional[str] = None,
|
94
|
+
max_concurrency: Optional[int] = None,
|
95
|
+
idle_timeout_s: Optional[int] = None,
|
96
|
+
) -> JobQueueStatus:
|
97
|
+
"""Update a job queue."""
|
98
|
+
|
99
|
+
if max_concurrency is None and idle_timeout_s is None:
|
100
|
+
raise ValueError("No fields to update")
|
101
|
+
|
102
|
+
jq = self._resolve_to_job_queue_model(
|
103
|
+
job_queue_id=job_queue_id, name=job_queue_name
|
104
|
+
)
|
105
|
+
|
106
|
+
assert jq.id is not None
|
107
|
+
updated_jq = self.client.update_job_queue(
|
108
|
+
job_queue_id=jq.id,
|
109
|
+
max_concurrency=max_concurrency,
|
110
|
+
idle_timeout_s=idle_timeout_s,
|
111
|
+
)
|
112
|
+
|
113
|
+
return _parse_decorated_jq_to_status(updated_jq)
|
114
|
+
|
115
|
+
def _resolve_to_job_queue_model(
|
116
|
+
self, *, job_queue_id: Optional[str] = None, name: Optional[str] = None,
|
117
|
+
) -> DecoratedJobQueue:
|
118
|
+
"""Finds the specific Job Queue API model by ID or name.
|
119
|
+
"""
|
120
|
+
if job_queue_id is None and name is None:
|
121
|
+
raise ValueError("Either 'job_queue_id' or 'name' must be provided.")
|
122
|
+
|
123
|
+
if job_queue_id:
|
124
|
+
job_queue = self.client.get_job_queue(job_queue_id)
|
125
|
+
if job_queue is None:
|
126
|
+
raise ValueError(f"Job Queue with ID '{job_queue_id}' not found.")
|
127
|
+
return job_queue
|
128
|
+
else:
|
129
|
+
job_queues = self.client.list_job_queues(name=name, count=1)
|
130
|
+
if len(job_queues) == 0:
|
131
|
+
raise ValueError(f"Job Queue with name '{name}' not found.")
|
132
|
+
return job_queues[0]
|
133
|
+
|
134
|
+
|
135
|
+
def _parse_decorated_jq_to_status(decorated_jq: DecoratedJobQueue) -> JobQueueStatus:
|
136
|
+
"""Helper to convert API model to SDK model."""
|
137
|
+
|
138
|
+
if decorated_jq.id is None or decorated_jq.current_job_queue_state is None:
|
139
|
+
raise ValueError("Job Queue ID or state is missing.")
|
140
|
+
|
141
|
+
return JobQueueStatus(
|
142
|
+
id=decorated_jq.id,
|
143
|
+
name=decorated_jq.name,
|
144
|
+
state=decorated_jq.current_job_queue_state,
|
145
|
+
creator_email=decorated_jq.creator_email,
|
146
|
+
project_id=decorated_jq.project_id,
|
147
|
+
created_at=decorated_jq.created_at,
|
148
|
+
max_concurrency=decorated_jq.max_concurrency,
|
149
|
+
idle_timeout_s=decorated_jq.idle_timeout_sec,
|
150
|
+
creator_id=decorated_jq.creator_id,
|
151
|
+
cloud_id=decorated_jq.cloud_id,
|
152
|
+
user_provided_id=decorated_jq.user_provided_id,
|
153
|
+
execution_mode=decorated_jq.execution_mode,
|
154
|
+
total_jobs=decorated_jq.total_jobs,
|
155
|
+
active_jobs=decorated_jq.active_jobs,
|
156
|
+
successful_jobs=decorated_jq.successful_jobs,
|
157
|
+
failed_jobs=decorated_jq.failed_jobs,
|
158
|
+
)
|
@@ -0,0 +1,130 @@
|
|
1
|
+
from typing import List, Optional
|
2
|
+
|
3
|
+
from anyscale._private.models.model_base import ResultIterator
|
4
|
+
from anyscale._private.sdk import sdk_command
|
5
|
+
from anyscale.client.openapi_client.models.job_queue_sort_directive import (
|
6
|
+
JobQueueSortDirective,
|
7
|
+
)
|
8
|
+
from anyscale.client.openapi_client.models.session_state import SessionState
|
9
|
+
from anyscale.job_queue._private.job_queue_sdk import PrivateJobQueueSDK
|
10
|
+
from anyscale.job_queue.models import JobQueueStatus
|
11
|
+
|
12
|
+
|
13
|
+
_JOB_QUEUE_SDK_SINGLETON_KEY = "job_queue_sdk"
|
14
|
+
|
15
|
+
|
16
|
+
_LIST_EXAMPLE = """
|
17
|
+
import anyscale
|
18
|
+
|
19
|
+
# Example: List the first 50 job queues
|
20
|
+
for jq in anyscale.job_queue.list(max_items=50):
|
21
|
+
print(jq.id, jq.name, jq.state)
|
22
|
+
"""
|
23
|
+
|
24
|
+
_LIST_ARG_DOCSTRINGS = {
|
25
|
+
"job_queue_id": "If provided, fetches only the job queue with this ID.",
|
26
|
+
"name": "Filter by job queue name.",
|
27
|
+
"creator_id": "Filter by the user ID of the creator.",
|
28
|
+
"cloud": "Filter by cloud name.",
|
29
|
+
"project": "Filter by project name.",
|
30
|
+
"cluster_status": "Filter by the state of the associated cluster.",
|
31
|
+
"page_size": "Number of items per API request page.",
|
32
|
+
"max_items": "Maximum total number of items to return.",
|
33
|
+
"sorting_directives": "List of directives to sort the results.",
|
34
|
+
}
|
35
|
+
|
36
|
+
_STATUS_EXAMPLE = """
|
37
|
+
import anyscale
|
38
|
+
|
39
|
+
status = anyscale.job_queue.status(job_queue_id=\"jobq_abc123\")
|
40
|
+
print(status)
|
41
|
+
"""
|
42
|
+
|
43
|
+
_STATUS_ARG_DOCSTRINGS = {
|
44
|
+
"job_queue_id": "The unique ID of the job queue.",
|
45
|
+
}
|
46
|
+
|
47
|
+
_UPDATE_EXAMPLE = """
|
48
|
+
import anyscale
|
49
|
+
|
50
|
+
updated_jq = anyscale.job_queue.update(job_queue_id=\"jobq_abc123\", max_concurrency=5)
|
51
|
+
print(updated_jq)
|
52
|
+
"""
|
53
|
+
|
54
|
+
_UPDATE_ARG_DOCSTRINGS = {
|
55
|
+
"job_queue_id": "ID of the job queue to update.",
|
56
|
+
"job_queue_name": "Name of the job queue to update (alternative to ID).",
|
57
|
+
"max_concurrency": "New maximum concurrency value.",
|
58
|
+
"idle_timeout_s": "New idle timeout in seconds.",
|
59
|
+
}
|
60
|
+
|
61
|
+
|
62
|
+
@sdk_command(
|
63
|
+
_JOB_QUEUE_SDK_SINGLETON_KEY,
|
64
|
+
PrivateJobQueueSDK,
|
65
|
+
doc_py_example=_LIST_EXAMPLE,
|
66
|
+
arg_docstrings=_LIST_ARG_DOCSTRINGS,
|
67
|
+
)
|
68
|
+
def list( # noqa: A001
|
69
|
+
*,
|
70
|
+
job_queue_id: Optional[str] = None,
|
71
|
+
name: Optional[str] = None,
|
72
|
+
creator_id: Optional[str] = None,
|
73
|
+
cloud: Optional[str] = None,
|
74
|
+
project: Optional[str] = None,
|
75
|
+
cluster_status: Optional[SessionState] = None,
|
76
|
+
page_size: Optional[int] = None,
|
77
|
+
max_items: Optional[int] = None,
|
78
|
+
sorting_directives: Optional[List[JobQueueSortDirective]] = None,
|
79
|
+
_private_sdk: Optional[PrivateJobQueueSDK] = None,
|
80
|
+
) -> ResultIterator[JobQueueStatus]:
|
81
|
+
"""List job queues or fetch a single job queue by ID."""
|
82
|
+
return _private_sdk.list( # type: ignore
|
83
|
+
job_queue_id=job_queue_id,
|
84
|
+
name=name,
|
85
|
+
creator_id=creator_id,
|
86
|
+
cloud=cloud,
|
87
|
+
project=project,
|
88
|
+
cluster_status=cluster_status,
|
89
|
+
page_size=page_size,
|
90
|
+
max_items=max_items,
|
91
|
+
sorting_directives=sorting_directives,
|
92
|
+
)
|
93
|
+
|
94
|
+
|
95
|
+
@sdk_command(
|
96
|
+
_JOB_QUEUE_SDK_SINGLETON_KEY,
|
97
|
+
PrivateJobQueueSDK,
|
98
|
+
doc_py_example=_STATUS_EXAMPLE,
|
99
|
+
arg_docstrings=_STATUS_ARG_DOCSTRINGS,
|
100
|
+
)
|
101
|
+
def status(
|
102
|
+
job_queue_id: str, _private_sdk: Optional[PrivateJobQueueSDK] = None
|
103
|
+
) -> JobQueueStatus:
|
104
|
+
"""Get the status and details for a specific job queue."""
|
105
|
+
return _private_sdk.status( # type: ignore
|
106
|
+
job_queue_id=job_queue_id
|
107
|
+
)
|
108
|
+
|
109
|
+
|
110
|
+
@sdk_command(
|
111
|
+
_JOB_QUEUE_SDK_SINGLETON_KEY,
|
112
|
+
PrivateJobQueueSDK,
|
113
|
+
doc_py_example=_UPDATE_EXAMPLE,
|
114
|
+
arg_docstrings=_UPDATE_ARG_DOCSTRINGS,
|
115
|
+
)
|
116
|
+
def update(
|
117
|
+
*,
|
118
|
+
job_queue_id: Optional[str] = None,
|
119
|
+
job_queue_name: Optional[str] = None,
|
120
|
+
max_concurrency: Optional[int] = None,
|
121
|
+
idle_timeout_s: Optional[int] = None,
|
122
|
+
_private_sdk: Optional[PrivateJobQueueSDK] = None,
|
123
|
+
) -> JobQueueStatus:
|
124
|
+
"""Update a job queue."""
|
125
|
+
return _private_sdk.update( # type: ignore
|
126
|
+
job_queue_id=job_queue_id,
|
127
|
+
job_queue_name=job_queue_name,
|
128
|
+
max_concurrency=max_concurrency,
|
129
|
+
idle_timeout_s=idle_timeout_s,
|
130
|
+
)
|