anyscale 0.26.15__py3-none-any.whl → 0.26.17__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 +4 -2
- anyscale/_private/anyscale_client/common.py +6 -4
- anyscale/_private/anyscale_client/fake_anyscale_client.py +15 -6
- anyscale/_private/docgen/__main__.py +4 -4
- anyscale/_private/docgen/generator.py +2 -2
- anyscale/_private/sdk/__init__.py +2 -2
- anyscale/_private/workload/workload_sdk.py +6 -4
- anyscale/aggregated_instance_usage/commands.py +6 -2
- anyscale/anyscale-cloud-setup-gcp.yaml +2 -0
- anyscale/client/README.md +11 -6
- anyscale/client/openapi_client/__init__.py +8 -4
- anyscale/client/openapi_client/api/default_api.py +402 -270
- anyscale/client/openapi_client/models/__init__.py +8 -4
- anyscale/client/openapi_client/models/alert_type.py +11 -2
- anyscale/client/openapi_client/models/create_job_queue_requests.py +3 -32
- anyscale/client/openapi_client/models/i_know_response.py +174 -0
- anyscale/client/openapi_client/models/i_know_time_series_event.py +148 -0
- anyscale/client/openapi_client/models/job_report.py +199 -0
- anyscale/client/openapi_client/models/job_with_report.py +254 -0
- anyscale/client/openapi_client/models/jobwithreport_list_response.py +147 -0
- anyscale/client/openapi_client/models/{product_autoscaler_flag.py → list_ray_sessions_response.py} +22 -23
- anyscale/client/openapi_client/models/{productautoscalerflag_response.py → listraysessionsresponse_response.py} +11 -11
- anyscale/client/openapi_client/models/metric.py +133 -3
- anyscale/client/openapi_client/models/ray_session.py +121 -0
- anyscale/cloud/__init__.py +2 -2
- anyscale/cloud/_private/cloud_sdk.py +2 -2
- anyscale/cloud/commands.py +9 -6
- anyscale/cloud_utils.py +5 -4
- anyscale/cluster_compute.py +2 -2
- anyscale/cluster_env.py +2 -0
- anyscale/commands/cloud_commands.py +43 -0
- anyscale/commands/login_commands.py +24 -3
- anyscale/commands/schedule_commands.py +2 -1
- anyscale/compute_config/commands.py +15 -7
- anyscale/controllers/cloud_controller.py +151 -8
- anyscale/controllers/cluster_controller.py +1 -0
- anyscale/controllers/job_controller.py +1 -1
- anyscale/controllers/service_controller.py +1 -0
- anyscale/image/commands.py +6 -6
- anyscale/job/_private/job_sdk.py +22 -24
- anyscale/job/commands.py +12 -12
- anyscale/organization_invitation/commands.py +11 -7
- anyscale/project/__init__.py +2 -2
- anyscale/project/_private/project_sdk.py +2 -2
- anyscale/project/commands.py +3 -3
- anyscale/project_utils.py +1 -1
- anyscale/resource_quota/commands.py +18 -10
- anyscale/schedule/commands.py +12 -8
- anyscale/sdk/anyscale_client/sdk.py +1 -0
- anyscale/service/commands.py +17 -17
- anyscale/service_account/commands.py +12 -10
- anyscale/user/commands.py +5 -3
- anyscale/utils/gcp_utils.py +25 -9
- anyscale/version.py +1 -1
- anyscale/workspace/__init__.py +1 -1
- anyscale/workspace/_private/workspace_sdk.py +5 -3
- anyscale/workspace/commands.py +26 -24
- {anyscale-0.26.15.dist-info → anyscale-0.26.17.dist-info}/METADATA +1 -1
- {anyscale-0.26.15.dist-info → anyscale-0.26.17.dist-info}/RECORD +64 -60
- anyscale/client/openapi_client/models/aviary_model_config_v2.py +0 -358
- anyscale/client/openapi_client/models/finish_ft_job_request_v2.py +0 -183
- {anyscale-0.26.15.dist-info → anyscale-0.26.17.dist-info}/LICENSE +0 -0
- {anyscale-0.26.15.dist-info → anyscale-0.26.17.dist-info}/NOTICE +0 -0
- {anyscale-0.26.15.dist-info → anyscale-0.26.17.dist-info}/WHEEL +0 -0
- {anyscale-0.26.15.dist-info → anyscale-0.26.17.dist-info}/entry_points.txt +0 -0
- {anyscale-0.26.15.dist-info → anyscale-0.26.17.dist-info}/top_level.txt +0 -0
@@ -3,6 +3,7 @@ Fetches data required and formats output for `anyscale cloud` commands.
|
|
3
3
|
"""
|
4
4
|
|
5
5
|
import copy
|
6
|
+
from datetime import datetime, timedelta
|
6
7
|
import json
|
7
8
|
from os import getenv
|
8
9
|
import pathlib
|
@@ -16,6 +17,7 @@ import boto3
|
|
16
17
|
from botocore.exceptions import ClientError, NoCredentialsError
|
17
18
|
import click
|
18
19
|
from click import Abort, ClickException
|
20
|
+
from rich.progress import Progress, track
|
19
21
|
import yaml
|
20
22
|
|
21
23
|
from anyscale import __version__ as anyscale_version
|
@@ -72,8 +74,12 @@ from anyscale.controllers.cloud_functional_verification_controller import (
|
|
72
74
|
CloudFunctionalVerificationType,
|
73
75
|
)
|
74
76
|
from anyscale.formatters import clouds_formatter
|
77
|
+
from anyscale.job._private.job_sdk import (
|
78
|
+
HA_JOB_STATE_TO_JOB_STATE,
|
79
|
+
TERMINAL_HA_JOB_STATES,
|
80
|
+
)
|
75
81
|
from anyscale.shared_anyscale_utils.aws import AwsRoleArn
|
76
|
-
from anyscale.shared_anyscale_utils.conf import ANYSCALE_ENV
|
82
|
+
from anyscale.shared_anyscale_utils.conf import ANYSCALE_ENV, ANYSCALE_HOST
|
77
83
|
from anyscale.util import ( # pylint:disable=private-import
|
78
84
|
_client,
|
79
85
|
_get_aws_efs_mount_target_ip,
|
@@ -1411,7 +1417,7 @@ class CloudController(BaseController):
|
|
1411
1417
|
)
|
1412
1418
|
if enable_log_ingestion is not None:
|
1413
1419
|
self._update_customer_aggregated_logs_config(
|
1414
|
-
cloud_id=cloud_id, is_enabled=enable_log_ingestion,
|
1420
|
+
cloud_id=cloud_id, is_enabled=enable_log_ingestion, # type: ignore
|
1415
1421
|
)
|
1416
1422
|
self.log.info(
|
1417
1423
|
f"Successfully updated log ingestion configuration for cloud, "
|
@@ -1552,7 +1558,7 @@ class CloudController(BaseController):
|
|
1552
1558
|
cloud_resource=cloud_resource,
|
1553
1559
|
boto3_session=boto3_session,
|
1554
1560
|
region=cloud.region,
|
1555
|
-
cloud_id=cloud_id,
|
1561
|
+
cloud_id=cloud_id, # type: ignore
|
1556
1562
|
is_bring_your_own_resource=cloud.is_bring_your_own_resource,
|
1557
1563
|
is_private_network=cloud.is_private_cloud
|
1558
1564
|
if cloud.is_private_cloud
|
@@ -1575,7 +1581,7 @@ class CloudController(BaseController):
|
|
1575
1581
|
project_id=project_id,
|
1576
1582
|
host_project_id=host_project_id,
|
1577
1583
|
region=cloud.region,
|
1578
|
-
cloud_id=cloud_id,
|
1584
|
+
cloud_id=cloud_id, # type: ignore
|
1579
1585
|
yes=False,
|
1580
1586
|
strict=strict,
|
1581
1587
|
is_private_service_cloud=cloud.is_private_service_cloud,
|
@@ -3539,8 +3545,8 @@ class CloudController(BaseController):
|
|
3539
3545
|
)
|
3540
3546
|
try:
|
3541
3547
|
self._edit_aws_cloud(
|
3542
|
-
cloud_id=cloud_id,
|
3543
|
-
cloud_name=cloud_name,
|
3548
|
+
cloud_id=cloud_id, # type: ignore
|
3549
|
+
cloud_name=cloud_name, # type: ignore
|
3544
3550
|
cloud=cloud,
|
3545
3551
|
cloud_resource=cloud_resource,
|
3546
3552
|
aws_s3_id=aws_s3_id,
|
@@ -3593,8 +3599,8 @@ class CloudController(BaseController):
|
|
3593
3599
|
)
|
3594
3600
|
try:
|
3595
3601
|
self._edit_gcp_cloud(
|
3596
|
-
cloud_id=cloud_id,
|
3597
|
-
cloud_name=cloud_name,
|
3602
|
+
cloud_id=cloud_id, # type: ignore
|
3603
|
+
cloud_name=cloud_name, # type: ignore
|
3598
3604
|
cloud=cloud,
|
3599
3605
|
cloud_resource=cloud_resource,
|
3600
3606
|
gcp_filestore_instance_id=gcp_filestore_instance_id,
|
@@ -3643,3 +3649,140 @@ class CloudController(BaseController):
|
|
3643
3649
|
)
|
3644
3650
|
|
3645
3651
|
### End of edit cloud ###
|
3652
|
+
|
3653
|
+
def generate_jobs_report(self, cloud_id: str, csv: bool, out_path: str) -> None:
|
3654
|
+
end_time = datetime.now()
|
3655
|
+
start_time = end_time - timedelta(days=7)
|
3656
|
+
|
3657
|
+
full_results = []
|
3658
|
+
paging_token: Optional[str] = None
|
3659
|
+
count_per_page = 20
|
3660
|
+
curr_page_results = None
|
3661
|
+
total_jobs: Optional[int] = None
|
3662
|
+
|
3663
|
+
with Progress() as progress:
|
3664
|
+
download_task = progress.add_task("Downloading jobs...", total=None)
|
3665
|
+
|
3666
|
+
while curr_page_results is None or len(curr_page_results) == count_per_page:
|
3667
|
+
response = self.api_client.list_job_reports_api_v2_job_reports_get(
|
3668
|
+
cloud_id,
|
3669
|
+
start_time=start_time,
|
3670
|
+
end_time=end_time,
|
3671
|
+
paging_token=paging_token,
|
3672
|
+
count=count_per_page,
|
3673
|
+
)
|
3674
|
+
curr_page_results = response.results
|
3675
|
+
full_results.extend(curr_page_results)
|
3676
|
+
paging_token = response.metadata.next_paging_token
|
3677
|
+
total_jobs = response.metadata.total
|
3678
|
+
progress.update(
|
3679
|
+
download_task, total=total_jobs, advance=len(curr_page_results)
|
3680
|
+
)
|
3681
|
+
|
3682
|
+
progress.update(download_task, completed=total_jobs)
|
3683
|
+
|
3684
|
+
if not full_results:
|
3685
|
+
self.log.info("No jobs found in the last 7 days.")
|
3686
|
+
return
|
3687
|
+
|
3688
|
+
filtered_results = [
|
3689
|
+
job
|
3690
|
+
for job in full_results
|
3691
|
+
if job.job_state in TERMINAL_HA_JOB_STATES and job.job_report is not None
|
3692
|
+
]
|
3693
|
+
filtered_results.sort(key=lambda x: x.created_at)
|
3694
|
+
|
3695
|
+
with open(out_path, "w") as out_file:
|
3696
|
+
if csv:
|
3697
|
+
out_file.write(
|
3698
|
+
"Job ID,Job name,Job state,Created at,Finished at,Duration,Unused CPU hours,Unused GPU hours,Max concurrent instances\n"
|
3699
|
+
)
|
3700
|
+
for job in track(filtered_results, description="Generating report..."):
|
3701
|
+
job_state = HA_JOB_STATE_TO_JOB_STATE[job.job_state]
|
3702
|
+
if job.finished_at is not None:
|
3703
|
+
duration = str(job.finished_at - job.created_at)
|
3704
|
+
finished_at = str(job.finished_at)
|
3705
|
+
else:
|
3706
|
+
duration = ""
|
3707
|
+
finished_at = ""
|
3708
|
+
unused_cpu_hours = job.job_report.unused_cpu_hours or ""
|
3709
|
+
unused_gpu_hours = job.job_report.unused_gpu_hours or ""
|
3710
|
+
max_instances_launched = job.job_report.max_instances_launched or ""
|
3711
|
+
|
3712
|
+
out_file.write(
|
3713
|
+
f"{job.job_id},{job.job_name},{job_state},{str(job.created_at)},{finished_at},{duration},{unused_cpu_hours},{unused_gpu_hours},{max_instances_launched}\n"
|
3714
|
+
)
|
3715
|
+
else:
|
3716
|
+
out_file.write(
|
3717
|
+
f"""
|
3718
|
+
<html>
|
3719
|
+
<head>
|
3720
|
+
<title>Jobs Report - {str(end_time)}</title>
|
3721
|
+
<style>
|
3722
|
+
table {{
|
3723
|
+
border: 1px solid black;
|
3724
|
+
border-collapse: collapse;
|
3725
|
+
}}
|
3726
|
+
th, td {{
|
3727
|
+
border: 1px solid black;
|
3728
|
+
border-collapse: collapse;
|
3729
|
+
padding: 8px;
|
3730
|
+
}}
|
3731
|
+
</style>
|
3732
|
+
</head>
|
3733
|
+
<body>
|
3734
|
+
<h1>Job Report - {str(end_time)}</h1>
|
3735
|
+
<p>Total jobs reported (finished jobs): {len(filtered_results)}</p>
|
3736
|
+
<p>Total jobs in the last 7 days: {total_jobs}</p>
|
3737
|
+
<table>
|
3738
|
+
<thead>
|
3739
|
+
<tr>
|
3740
|
+
<th>Job ID</th>
|
3741
|
+
<th>Job name</th>
|
3742
|
+
<th>Job state</th>
|
3743
|
+
<th>Created at</th>
|
3744
|
+
<th>Finished at</th>
|
3745
|
+
<th>Duration</th>
|
3746
|
+
<th>Unused CPU hours</th>
|
3747
|
+
<th>Unused GPU hours</th>
|
3748
|
+
<th>Max concurrent instances</th>
|
3749
|
+
</tr>
|
3750
|
+
</thead>
|
3751
|
+
<tbody>
|
3752
|
+
"""
|
3753
|
+
)
|
3754
|
+
|
3755
|
+
for job in track(filtered_results, description="Generating report..."):
|
3756
|
+
job_state = HA_JOB_STATE_TO_JOB_STATE[job.job_state]
|
3757
|
+
if job.finished_at is not None:
|
3758
|
+
duration = str(job.finished_at - job.created_at)
|
3759
|
+
finished_at = str(job.finished_at)
|
3760
|
+
else:
|
3761
|
+
duration = ""
|
3762
|
+
finished_at = ""
|
3763
|
+
unused_cpu_hours = job.job_report.unused_cpu_hours or ""
|
3764
|
+
unused_gpu_hours = job.job_report.unused_gpu_hours or ""
|
3765
|
+
max_instances_launched = job.job_report.max_instances_launched or ""
|
3766
|
+
|
3767
|
+
out_file.write(
|
3768
|
+
f"""
|
3769
|
+
<tr>
|
3770
|
+
<td><a target="_blank" rel="noreferrer" href="{ANYSCALE_HOST}/jobs/{job.job_id}">{job.job_id}</a></td>
|
3771
|
+
<td>{job.job_name}</td>
|
3772
|
+
<td>{job_state}</td>
|
3773
|
+
<td>{str(job.created_at)}</td>
|
3774
|
+
<td>{finished_at}</td>
|
3775
|
+
<td>{duration}</td>
|
3776
|
+
<td>{unused_cpu_hours}</td>
|
3777
|
+
<td>{unused_gpu_hours}</td>
|
3778
|
+
<td>{max_instances_launched}</td>
|
3779
|
+
</tr>
|
3780
|
+
"""
|
3781
|
+
)
|
3782
|
+
|
3783
|
+
out_file.write(
|
3784
|
+
"""
|
3785
|
+
</tbody>
|
3786
|
+
</table>
|
3787
|
+
"""
|
3788
|
+
)
|
@@ -522,6 +522,7 @@ class ClusterController(BaseController):
|
|
522
522
|
api_client=self.api_client,
|
523
523
|
anyscale_api_client=self.anyscale_api_client,
|
524
524
|
)
|
525
|
+
assert project_id is not None
|
525
526
|
cluster_name = self._get_or_generate_cluster_name(project_id, cluster_name)
|
526
527
|
return project_id, cluster_name
|
527
528
|
|
@@ -80,7 +80,7 @@ class JobController(BaseController):
|
|
80
80
|
log: Optional[LogsLogger] = None,
|
81
81
|
initialize_auth_api_client: bool = True,
|
82
82
|
raise_structured_exception: bool = False,
|
83
|
-
auth_token: str = None,
|
83
|
+
auth_token: Optional[str] = None,
|
84
84
|
):
|
85
85
|
if log is None:
|
86
86
|
log = LogsLogger()
|
@@ -354,6 +354,7 @@ class ServiceController(BaseController):
|
|
354
354
|
)
|
355
355
|
|
356
356
|
else:
|
357
|
+
assert ray_serve_config is not None
|
357
358
|
ray_serve_config["runtime_env"] = override_runtime_env_config(
|
358
359
|
runtime_env=ray_serve_config.get("runtime_env"),
|
359
360
|
anyscale_api_client=self.sdk,
|
anyscale/image/commands.py
CHANGED
@@ -36,13 +36,13 @@ def build(
|
|
36
36
|
*,
|
37
37
|
name: str,
|
38
38
|
ray_version: Optional[str] = None,
|
39
|
-
|
39
|
+
_private_sdk: Optional[PrivateImageSDK] = None,
|
40
40
|
) -> str:
|
41
41
|
"""Build an image from a Containerfile.
|
42
42
|
|
43
43
|
Returns the URI of the image.
|
44
44
|
"""
|
45
|
-
return
|
45
|
+
return _private_sdk.build_image_from_containerfile_with_image_uri( # type: ignore
|
46
46
|
name, containerfile, ray_version=ray_version
|
47
47
|
)
|
48
48
|
|
@@ -68,12 +68,12 @@ _GET_ARG_DOCSTRINGS = {
|
|
68
68
|
doc_py_example=_GET_EXAMPLE,
|
69
69
|
arg_docstrings=_GET_ARG_DOCSTRINGS,
|
70
70
|
)
|
71
|
-
def get(*, name: str,
|
71
|
+
def get(*, name: str, _private_sdk: Optional[PrivateImageSDK] = None) -> ImageBuild:
|
72
72
|
"""The name can contain an optional version tag, i.e., 'name:version'.
|
73
73
|
|
74
74
|
If no version is provided, the latest one will be returned.
|
75
75
|
"""
|
76
|
-
return
|
76
|
+
return _private_sdk.get(name) # type: ignore
|
77
77
|
|
78
78
|
|
79
79
|
_REGISTER_EXAMPLE = """
|
@@ -102,14 +102,14 @@ def register(
|
|
102
102
|
name: str,
|
103
103
|
ray_version: Optional[str] = None,
|
104
104
|
registry_login_secret: Optional[str] = None,
|
105
|
-
|
105
|
+
_private_sdk: Optional[PrivateImageSDK] = None,
|
106
106
|
) -> str:
|
107
107
|
"""
|
108
108
|
Registers a BYOD image with a container image name.
|
109
109
|
|
110
110
|
Returns the URI of the image.
|
111
111
|
"""
|
112
|
-
return
|
112
|
+
return _private_sdk.register_byod_image_with_name( # type: ignore
|
113
113
|
image_uri,
|
114
114
|
registry_login_secret=registry_login_secret,
|
115
115
|
ray_version=ray_version,
|
anyscale/job/_private/job_sdk.py
CHANGED
@@ -33,6 +33,26 @@ from anyscale.utils.runtime_env import parse_requirements_file
|
|
33
33
|
|
34
34
|
logger = BlockLogger()
|
35
35
|
|
36
|
+
HA_JOB_STATE_TO_JOB_STATE = {
|
37
|
+
HaJobStates.UPDATING: JobState.RUNNING,
|
38
|
+
HaJobStates.RUNNING: JobState.RUNNING,
|
39
|
+
HaJobStates.RESTARTING: JobState.RUNNING,
|
40
|
+
HaJobStates.CLEANING_UP: JobState.RUNNING,
|
41
|
+
HaJobStates.PENDING: JobState.STARTING,
|
42
|
+
HaJobStates.AWAITING_CLUSTER_START: JobState.STARTING,
|
43
|
+
HaJobStates.SUCCESS: JobState.SUCCEEDED,
|
44
|
+
HaJobStates.ERRORED: JobState.FAILED,
|
45
|
+
HaJobStates.TERMINATED: JobState.FAILED,
|
46
|
+
HaJobStates.BROKEN: JobState.FAILED,
|
47
|
+
HaJobStates.OUT_OF_RETRIES: JobState.FAILED,
|
48
|
+
}
|
49
|
+
|
50
|
+
TERMINAL_HA_JOB_STATES = [
|
51
|
+
HaJobStates.SUCCESS,
|
52
|
+
HaJobStates.TERMINATED,
|
53
|
+
HaJobStates.OUT_OF_RETRIES,
|
54
|
+
]
|
55
|
+
|
36
56
|
|
37
57
|
class PrivateJobSDK(WorkloadSDK):
|
38
58
|
_POLLING_INTERVAL_SECONDS = 10.0
|
@@ -215,20 +235,6 @@ class PrivateJobSDK(WorkloadSDK):
|
|
215
235
|
)
|
216
236
|
return job.id
|
217
237
|
|
218
|
-
_HA_JOB_STATE_TO_JOB_STATE = {
|
219
|
-
HaJobStates.UPDATING: JobState.RUNNING,
|
220
|
-
HaJobStates.RUNNING: JobState.RUNNING,
|
221
|
-
HaJobStates.RESTARTING: JobState.RUNNING,
|
222
|
-
HaJobStates.CLEANING_UP: JobState.RUNNING,
|
223
|
-
HaJobStates.PENDING: JobState.STARTING,
|
224
|
-
HaJobStates.AWAITING_CLUSTER_START: JobState.STARTING,
|
225
|
-
HaJobStates.SUCCESS: JobState.SUCCEEDED,
|
226
|
-
HaJobStates.ERRORED: JobState.FAILED,
|
227
|
-
HaJobStates.TERMINATED: JobState.FAILED,
|
228
|
-
HaJobStates.BROKEN: JobState.FAILED,
|
229
|
-
HaJobStates.OUT_OF_RETRIES: JobState.FAILED,
|
230
|
-
}
|
231
|
-
|
232
238
|
_BACKEND_JOB_STATUS_TO_JOB_RUN_STATE = {
|
233
239
|
BackendJobStatus.RUNNING: JobRunState.RUNNING,
|
234
240
|
BackendJobStatus.COMPLETED: JobRunState.SUCCEEDED,
|
@@ -241,9 +247,7 @@ class PrivateJobSDK(WorkloadSDK):
|
|
241
247
|
|
242
248
|
def _job_state_from_job_model(self, model: ProductionJob) -> JobState:
|
243
249
|
ha_state = model.state.current_state if model.state else None
|
244
|
-
return cast(
|
245
|
-
JobState, self._HA_JOB_STATE_TO_JOB_STATE.get(ha_state, JobState.UNKNOWN)
|
246
|
-
)
|
250
|
+
return cast(JobState, HA_JOB_STATE_TO_JOB_STATE.get(ha_state, JobState.UNKNOWN))
|
247
251
|
|
248
252
|
def _job_run_model_to_job_run_status(self, run: Job) -> JobRunStatus:
|
249
253
|
state = self._BACKEND_JOB_STATUS_TO_JOB_RUN_STATE.get(
|
@@ -358,12 +362,6 @@ class PrivateJobSDK(WorkloadSDK):
|
|
358
362
|
self.logger.info(f"Marked job '{job_model.name}' for termination")
|
359
363
|
return job_model.id
|
360
364
|
|
361
|
-
_TERMINAL_HA_JOB_STATES = [
|
362
|
-
HaJobStates.SUCCESS,
|
363
|
-
HaJobStates.TERMINATED,
|
364
|
-
HaJobStates.OUT_OF_RETRIES,
|
365
|
-
]
|
366
|
-
|
367
365
|
def archive(
|
368
366
|
self,
|
369
367
|
*,
|
@@ -377,7 +375,7 @@ class PrivateJobSDK(WorkloadSDK):
|
|
377
375
|
)
|
378
376
|
|
379
377
|
ha_state = job_model.state.current_state if job_model.state else None
|
380
|
-
if ha_state not in
|
378
|
+
if ha_state not in TERMINAL_HA_JOB_STATES:
|
381
379
|
raise RuntimeError(
|
382
380
|
f"Job with ID '{job_model.id}' has not reached a terminal state and cannot be archived."
|
383
381
|
)
|
anyscale/job/commands.py
CHANGED
@@ -54,12 +54,12 @@ _SUBMIT_ARG_DOCSTRINGS = {"config": "The config options defining the job."}
|
|
54
54
|
doc_py_example=_SUBMIT_EXAMPLE,
|
55
55
|
arg_docstrings=_SUBMIT_ARG_DOCSTRINGS,
|
56
56
|
)
|
57
|
-
def submit(config: JobConfig, *,
|
57
|
+
def submit(config: JobConfig, *, _private_sdk: Optional[PrivateJobSDK] = None) -> str:
|
58
58
|
"""Submit a job.
|
59
59
|
|
60
60
|
Returns the id of the submitted job.
|
61
61
|
"""
|
62
|
-
return
|
62
|
+
return _private_sdk.submit(config) # type: ignore
|
63
63
|
|
64
64
|
|
65
65
|
_STATUS_EXAMPLE = """
|
@@ -89,12 +89,12 @@ def status(
|
|
89
89
|
id: Optional[str] = None, # noqa: A002
|
90
90
|
cloud: Optional[str] = None,
|
91
91
|
project: Optional[str] = None,
|
92
|
-
|
92
|
+
_private_sdk: Optional[PrivateJobSDK] = None,
|
93
93
|
**_kwargs: Dict[str, Any],
|
94
94
|
) -> JobStatus:
|
95
95
|
"""Get the status of a job."""
|
96
96
|
id = _resolve_id_from_args(id, _kwargs) # noqa: A001
|
97
|
-
return
|
97
|
+
return _private_sdk.status(name=name, job_id=id, cloud=cloud, project=project) # type: ignore
|
98
98
|
|
99
99
|
|
100
100
|
_TERMINATE_EXAMPLE = """
|
@@ -123,7 +123,7 @@ def terminate(
|
|
123
123
|
id: Optional[str] = None, # noqa: A002
|
124
124
|
cloud: Optional[str] = None,
|
125
125
|
project: Optional[str] = None,
|
126
|
-
|
126
|
+
_private_sdk: Optional[PrivateJobSDK] = None,
|
127
127
|
**_kwargs: Dict[str, Any],
|
128
128
|
) -> str:
|
129
129
|
"""Terminate a job.
|
@@ -133,7 +133,7 @@ def terminate(
|
|
133
133
|
Returns the id of the terminated job.
|
134
134
|
"""
|
135
135
|
id = _resolve_id_from_args(id, _kwargs) # noqa: A001
|
136
|
-
return
|
136
|
+
return _private_sdk.terminate(name=name, job_id=id, cloud=cloud, project=project) # type: ignore
|
137
137
|
|
138
138
|
|
139
139
|
_ARCHIVE_EXAMPLE = """
|
@@ -162,7 +162,7 @@ def archive(
|
|
162
162
|
id: Optional[str] = None, # noqa: A002
|
163
163
|
cloud: Optional[str] = None,
|
164
164
|
project: Optional[str] = None,
|
165
|
-
|
165
|
+
_private_sdk: Optional[PrivateJobSDK] = None,
|
166
166
|
**_kwargs: Dict[str, Any],
|
167
167
|
) -> str:
|
168
168
|
"""Archive a job.
|
@@ -172,7 +172,7 @@ def archive(
|
|
172
172
|
Returns the id of the archived job.
|
173
173
|
"""
|
174
174
|
id = _resolve_id_from_args(id, _kwargs) # noqa: A001
|
175
|
-
return
|
175
|
+
return _private_sdk.archive(name=name, job_id=id, cloud=cloud, project=project) # type: ignore
|
176
176
|
|
177
177
|
|
178
178
|
_WAIT_EXAMPLE = """\
|
@@ -204,12 +204,12 @@ def wait(
|
|
204
204
|
project: Optional[str] = None,
|
205
205
|
state: Union[JobState, str] = JobState.SUCCEEDED,
|
206
206
|
timeout_s: float = 1800,
|
207
|
-
|
207
|
+
_private_sdk: Optional[PrivateJobSDK] = None,
|
208
208
|
**_kwargs: Dict[str, Any],
|
209
209
|
):
|
210
210
|
""""Wait for a job to enter a specific state."""
|
211
211
|
id = _resolve_id_from_args(id, _kwargs) # noqa: A001
|
212
|
-
|
212
|
+
_private_sdk.wait( # type: ignore
|
213
213
|
name=name,
|
214
214
|
job_id=id,
|
215
215
|
cloud=cloud,
|
@@ -251,12 +251,12 @@ def get_logs(
|
|
251
251
|
run: Optional[str] = None,
|
252
252
|
mode: Union[str, JobLogMode] = JobLogMode.TAIL,
|
253
253
|
max_lines: Optional[int] = None,
|
254
|
-
|
254
|
+
_private_sdk: Optional[PrivateJobSDK] = None,
|
255
255
|
**_kwargs: Dict[str, Any],
|
256
256
|
) -> str:
|
257
257
|
"""Query the jobs for a job run."""
|
258
258
|
id = _resolve_id_from_args(id, _kwargs) # noqa: A001
|
259
|
-
return
|
259
|
+
return _private_sdk.get_logs( # type: ignore
|
260
260
|
job_id=id,
|
261
261
|
name=name,
|
262
262
|
cloud=cloud,
|
@@ -1,4 +1,4 @@
|
|
1
|
-
from typing import Dict, List, Tuple
|
1
|
+
from typing import Dict, List, Optional, Tuple
|
2
2
|
|
3
3
|
from anyscale._private.sdk import sdk_command
|
4
4
|
from anyscale.organization_invitation._private.organization_invitation_sdk import (
|
@@ -45,13 +45,15 @@ _DELETE_ARG_DOCSTRINGS = {
|
|
45
45
|
arg_docstrings=_CREATE_ARG_DOCSTRINGS,
|
46
46
|
)
|
47
47
|
def create(
|
48
|
-
emails: List[str],
|
48
|
+
emails: List[str],
|
49
|
+
*,
|
50
|
+
_private_sdk: Optional[PrivateOrganizationInvitationSDK] = None
|
49
51
|
) -> Tuple[List[str], List[str]]:
|
50
52
|
"""Creates organization invitations for the provided emails.
|
51
53
|
|
52
54
|
Returns a tuple of successful emails and error messages.
|
53
55
|
"""
|
54
|
-
return
|
56
|
+
return _private_sdk.create(emails=emails) # type: ignore
|
55
57
|
|
56
58
|
|
57
59
|
@sdk_command(
|
@@ -61,13 +63,13 @@ def create(
|
|
61
63
|
arg_docstrings=_LIST_ARG_DOCSTRINGS,
|
62
64
|
)
|
63
65
|
def list( # noqa: A001
|
64
|
-
*,
|
66
|
+
*, _private_sdk: Optional[PrivateOrganizationInvitationSDK] = None
|
65
67
|
) -> List[OrganizationInvitation]:
|
66
68
|
"""Lists organization invitations.
|
67
69
|
|
68
70
|
Returns a list of organization invitations.
|
69
71
|
"""
|
70
|
-
return
|
72
|
+
return _private_sdk.list() # type: ignore
|
71
73
|
|
72
74
|
|
73
75
|
@sdk_command(
|
@@ -76,9 +78,11 @@ def list( # noqa: A001
|
|
76
78
|
doc_py_example=_DELETE_EXAMPLE,
|
77
79
|
arg_docstrings=_DELETE_ARG_DOCSTRINGS,
|
78
80
|
)
|
79
|
-
def delete(
|
81
|
+
def delete(
|
82
|
+
email: str, *, _private_sdk: Optional[PrivateOrganizationInvitationSDK] = None
|
83
|
+
) -> str:
|
80
84
|
"""Deletes an organization invitation.
|
81
85
|
|
82
86
|
Returns the email of the deleted organization invitation.
|
83
87
|
"""
|
84
|
-
return
|
88
|
+
return _private_sdk.delete(email) # type: ignore
|
anyscale/project/__init__.py
CHANGED
@@ -29,7 +29,7 @@ class ProjectSDK:
|
|
29
29
|
)
|
30
30
|
def add_collaborators( # noqa: F811
|
31
31
|
self, cloud: str, project: str, collaborators: List[CreateProjectCollaborator],
|
32
|
-
) ->
|
32
|
+
) -> None:
|
33
33
|
"""Batch add collaborators to a project.
|
34
34
|
"""
|
35
|
-
|
35
|
+
self._private_sdk.add_collaborators(cloud, project, collaborators)
|
@@ -11,11 +11,11 @@ from anyscale.project.models import CreateProjectCollaborator
|
|
11
11
|
class PrivateProjectSDK(BaseSDK):
|
12
12
|
def add_collaborators(
|
13
13
|
self, cloud: str, project: str, collaborators: List[CreateProjectCollaborator]
|
14
|
-
) ->
|
14
|
+
) -> None:
|
15
15
|
cloud_id = self.client.get_cloud_id(cloud_name=cloud, compute_config_id=None)
|
16
16
|
project_id = self.client.get_project_id(parent_cloud_id=cloud_id, name=project)
|
17
17
|
|
18
|
-
|
18
|
+
self.client.add_project_collaborators(
|
19
19
|
project_id=project_id,
|
20
20
|
collaborators=[
|
21
21
|
CreateUserProjectCollaborator(
|
anyscale/project/commands.py
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
from typing import List
|
1
|
+
from typing import List, Optional
|
2
2
|
|
3
3
|
from anyscale._private.sdk import sdk_command
|
4
4
|
from anyscale.project._private.project_sdk import PrivateProjectSDK
|
@@ -49,8 +49,8 @@ def add_collaborators(
|
|
49
49
|
project: str,
|
50
50
|
collaborators: List[CreateProjectCollaborator],
|
51
51
|
*,
|
52
|
-
|
52
|
+
_private_sdk: Optional[PrivateProjectSDK] = None
|
53
53
|
) -> str:
|
54
54
|
"""Batch add collaborators to a project.
|
55
55
|
"""
|
56
|
-
return
|
56
|
+
return _private_sdk.add_collaborators(cloud, project, collaborators) # type: ignore
|
anyscale/project_utils.py
CHANGED
@@ -442,7 +442,7 @@ def get_default_project(
|
|
442
442
|
"Please specify a cloud for this command either through the cloud or "
|
443
443
|
"compute config arguments."
|
444
444
|
)
|
445
|
-
default_project = anyscale_api_client.get_default_project(
|
445
|
+
default_project = anyscale_api_client.get_default_project( # type: ignore
|
446
446
|
parent_cloud_id=parent_cloud_id
|
447
447
|
).result
|
448
448
|
return default_project
|
@@ -87,11 +87,13 @@ _DISABLE_DOCSTRINGS = {
|
|
87
87
|
arg_docstrings=_CREATE_DOCSTRINGS,
|
88
88
|
)
|
89
89
|
def create(
|
90
|
-
create_resource_quota: CreateResourceQuota,
|
90
|
+
create_resource_quota: CreateResourceQuota,
|
91
|
+
*,
|
92
|
+
_private_sdk: Optional[PrivateResourceQuotaSDK] = None
|
91
93
|
) -> ResourceQuota:
|
92
94
|
"""Create a resource quota.
|
93
95
|
"""
|
94
|
-
return
|
96
|
+
return _private_sdk.create(create_resource_quota) # type: ignore
|
95
97
|
|
96
98
|
|
97
99
|
@sdk_command(
|
@@ -107,10 +109,10 @@ def list( # noqa: A001
|
|
107
109
|
is_enabled: Optional[bool] = None,
|
108
110
|
max_items: int = 20,
|
109
111
|
*,
|
110
|
-
|
112
|
+
_private_sdk: Optional[PrivateResourceQuotaSDK] = None
|
111
113
|
) -> List[ResourceQuota]:
|
112
114
|
"""List resource quotas. """
|
113
|
-
return
|
115
|
+
return _private_sdk.list(name, cloud, creator_id, is_enabled, max_items,) # type: ignore
|
114
116
|
|
115
117
|
|
116
118
|
@sdk_command(
|
@@ -119,10 +121,12 @@ def list( # noqa: A001
|
|
119
121
|
doc_py_example=_DELETE_EXAMPLE,
|
120
122
|
arg_docstrings=_DELETE_DOCSTRINGS,
|
121
123
|
)
|
122
|
-
def delete(
|
124
|
+
def delete(
|
125
|
+
resource_quota_id: str, *, _private_sdk: Optional[PrivateResourceQuotaSDK] = None
|
126
|
+
):
|
123
127
|
"""Delete a resource quota.
|
124
128
|
"""
|
125
|
-
return
|
129
|
+
return _private_sdk.delete(resource_quota_id) # type: ignore
|
126
130
|
|
127
131
|
|
128
132
|
@sdk_command(
|
@@ -131,10 +135,12 @@ def delete(resource_quota_id: str, *, _sdk: PrivateResourceQuotaSDK):
|
|
131
135
|
doc_py_example=_ENABLE_EXAMPLE,
|
132
136
|
arg_docstrings=_ENABLE_DOCSTRINGS,
|
133
137
|
)
|
134
|
-
def enable(
|
138
|
+
def enable(
|
139
|
+
resource_quota_id: str, *, _private_sdk: Optional[PrivateResourceQuotaSDK] = None
|
140
|
+
):
|
135
141
|
"""Enable a resource quota.
|
136
142
|
"""
|
137
|
-
return
|
143
|
+
return _private_sdk.set_status(resource_quota_id, True) # type: ignore
|
138
144
|
|
139
145
|
|
140
146
|
@sdk_command(
|
@@ -143,7 +149,9 @@ def enable(resource_quota_id: str, *, _sdk: PrivateResourceQuotaSDK):
|
|
143
149
|
doc_py_example=_DISABLE_EXAMPLE,
|
144
150
|
arg_docstrings=_DISABLE_DOCSTRINGS,
|
145
151
|
)
|
146
|
-
def disable(
|
152
|
+
def disable(
|
153
|
+
resource_quota_id: str, *, _private_sdk: Optional[PrivateResourceQuotaSDK] = None
|
154
|
+
):
|
147
155
|
"""Disable a resource quota.
|
148
156
|
"""
|
149
|
-
return
|
157
|
+
return _private_sdk.set_status(resource_quota_id, False) # type: ignore
|