Skip to content

Commit 3546eab

Browse files
committed
Add InstanceOffer.backend_data + reserved GCP A4
Add an offer field for holding arbitrary backend-specific data. As an example, use this field to mark reserved GCP A4 instances.
1 parent eddf99d commit 3546eab

File tree

6 files changed

+53
-6
lines changed

6 files changed

+53
-6
lines changed

pyproject.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ dependencies = [
3232
"python-multipart>=0.0.16",
3333
"filelock",
3434
"psutil",
35-
"gpuhunt==0.1.10",
35+
"gpuhunt @ https://github.com/dstackai/gpuhunt/archive/refs/heads/provider_data_typed_dict.zip",
3636
"argcomplete>=3.5.0",
3737
"ignore-python>=0.2.0",
3838
"orjson",
@@ -67,6 +67,9 @@ artifacts = [
6767
"src/dstack/_internal/server/statics/**",
6868
]
6969

70+
[tool.hatch.metadata]
71+
allow-direct-references = true
72+
7073
[tool.hatch.metadata.hooks.fancy-pypi-readme]
7174
content-type = "text/markdown"
7275

src/dstack/_internal/core/backends/base/offers.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
"lambda-arm",
2525
"gcp-a4",
2626
"gcp-g4-preview",
27+
"gcp-dws-calendar-mode",
2728
]
2829

2930

@@ -94,6 +95,7 @@ def catalog_item_to_offer(
9495
),
9596
region=item.location,
9697
price=item.price,
98+
backend_data=item.provider_data,
9799
)
98100

99101

src/dstack/_internal/core/backends/gcp/compute.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,10 @@
9090
TPU_VERSIONS = [tpu.name for tpu in KNOWN_TPUS]
9191

9292

93+
class GCPOfferBackendData(CoreModel):
94+
is_dws_calendar_mode: bool = False
95+
96+
9397
class GCPVolumeDiskBackendData(CoreModel):
9498
type: Literal["disk"] = "disk"
9599
disk_type: str
@@ -202,6 +206,23 @@ def reservation_modifier(
202206
modifiers.append(get_offers_disk_modifier(CONFIGURABLE_DISK_SIZE, requirements))
203207
return modifiers
204208

209+
def get_offers_post_filter(
210+
self, requirements: Requirements
211+
) -> Optional[Callable[[InstanceOfferWithAvailability], bool]]:
212+
if requirements.reservation is None:
213+
214+
def reserved_offers_filter(offer: InstanceOfferWithAvailability) -> bool:
215+
"""Remove reserved-only offers"""
216+
if GCPOfferBackendData.__response__.parse_obj(
217+
offer.backend_data
218+
).is_dws_calendar_mode:
219+
return False
220+
return True
221+
222+
return reserved_offers_filter
223+
224+
return None
225+
205226
def terminate_instance(
206227
self, instance_id: str, region: str, backend_data: Optional[str] = None
207228
) -> None:

src/dstack/_internal/core/compatibility/runs.py

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,20 @@ def get_apply_plan_excludes(plan: ApplyRunPlanInput) -> Optional[IncludeExcludeD
4949
job_submissions_excludes["job_provisioning_data"] = {
5050
"instance_type": {"resources": {"cpu_arch"}}
5151
}
52+
jrd_offer_excludes = {}
53+
if any(
54+
js.job_runtime_data and js.job_runtime_data.offer for js in job_submissions
55+
) and all(
56+
not js.job_runtime_data
57+
or not js.job_runtime_data.offer
58+
or not js.job_runtime_data.offer.backend_data
59+
for js in job_submissions
60+
):
61+
jrd_offer_excludes["backend_data"] = True
5262
if all(map(_should_exclude_job_submission_jrd_cpu_arch, job_submissions)):
53-
job_submissions_excludes["job_runtime_data"] = {
54-
"offer": {"instance": {"resources": {"cpu_arch"}}}
55-
}
63+
jrd_offer_excludes["instance"] = {"resources": {"cpu_arch"}}
64+
if jrd_offer_excludes:
65+
job_submissions_excludes["job_runtime_data"] = {"offer": jrd_offer_excludes}
5666
if all(js.exit_status is None for js in job_submissions):
5767
job_submissions_excludes["exit_status"] = True
5868
if all(js.status_message == "" for js in job_submissions):
@@ -71,9 +81,18 @@ def get_apply_plan_excludes(plan: ApplyRunPlanInput) -> Optional[IncludeExcludeD
7181
latest_job_submission_excludes["job_provisioning_data"] = {
7282
"instance_type": {"resources": {"cpu_arch"}}
7383
}
84+
latest_job_submission_jrd_offer_excludes = {}
85+
if (
86+
latest_job_submission.job_runtime_data
87+
and latest_job_submission.job_runtime_data.offer
88+
and not latest_job_submission.job_runtime_data.offer.backend_data
89+
):
90+
latest_job_submission_jrd_offer_excludes["backend_data"] = True
7491
if _should_exclude_job_submission_jrd_cpu_arch(latest_job_submission):
92+
latest_job_submission_jrd_offer_excludes["instance"] = {"resources": {"cpu_arch"}}
93+
if latest_job_submission_jrd_offer_excludes:
7594
latest_job_submission_excludes["job_runtime_data"] = {
76-
"offer": {"instance": {"resources": {"cpu_arch"}}}
95+
"offer": latest_job_submission_jrd_offer_excludes
7796
}
7897
if latest_job_submission.exit_status is None:
7998
latest_job_submission_excludes["exit_status"] = True

src/dstack/_internal/core/models/instances.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import datetime
22
from enum import Enum
3-
from typing import Dict, List, Optional
3+
from typing import Any, Dict, List, Optional
44
from uuid import UUID
55

66
import gpuhunt
@@ -184,6 +184,7 @@ class InstanceOffer(CoreModel):
184184
instance: InstanceType
185185
region: str
186186
price: float
187+
backend_data: dict[str, Any] = {}
187188

188189

189190
class InstanceOfferWithAvailability(InstanceOffer):

src/dstack/_internal/server/services/offers.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ def generate_shared_offer(
215215
),
216216
region=offer.region,
217217
price=offer.price,
218+
backend_data=offer.backend_data,
218219
availability=offer.availability,
219220
blocks=blocks,
220221
total_blocks=total_blocks,

0 commit comments

Comments
 (0)