dstack 0.18.44__py3-none-any.whl → 0.19.0rc1__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.
- dstack/_internal/cli/commands/gateway.py +15 -3
- dstack/_internal/cli/commands/logs.py +0 -22
- dstack/_internal/cli/commands/stats.py +8 -17
- dstack/_internal/cli/main.py +1 -5
- dstack/_internal/cli/services/configurators/fleet.py +4 -39
- dstack/_internal/cli/services/configurators/run.py +22 -21
- dstack/_internal/cli/services/profile.py +34 -83
- dstack/_internal/cli/utils/gateway.py +1 -1
- dstack/_internal/core/backends/__init__.py +56 -39
- dstack/_internal/core/backends/aws/__init__.py +0 -25
- dstack/_internal/core/backends/aws/auth.py +1 -10
- dstack/_internal/core/backends/aws/backend.py +26 -0
- dstack/_internal/core/backends/aws/compute.py +20 -45
- dstack/_internal/{server/services/backends/configurators/aws.py → core/backends/aws/configurator.py} +46 -85
- dstack/_internal/core/backends/aws/models.py +135 -0
- dstack/_internal/core/backends/aws/resources.py +1 -1
- dstack/_internal/core/backends/azure/__init__.py +0 -20
- dstack/_internal/core/backends/azure/auth.py +2 -11
- dstack/_internal/core/backends/azure/backend.py +21 -0
- dstack/_internal/core/backends/azure/compute.py +13 -27
- dstack/_internal/{server/services/backends/configurators/azure.py → core/backends/azure/configurator.py} +141 -210
- dstack/_internal/core/backends/azure/models.py +89 -0
- dstack/_internal/core/backends/base/__init__.py +0 -12
- dstack/_internal/core/backends/base/backend.py +18 -0
- dstack/_internal/core/backends/base/compute.py +153 -33
- dstack/_internal/core/backends/base/configurator.py +105 -0
- dstack/_internal/core/backends/base/models.py +14 -0
- dstack/_internal/core/backends/configurators.py +138 -0
- dstack/_internal/core/backends/cudo/__init__.py +0 -15
- dstack/_internal/core/backends/cudo/backend.py +16 -0
- dstack/_internal/core/backends/cudo/compute.py +8 -26
- dstack/_internal/core/backends/cudo/configurator.py +72 -0
- dstack/_internal/core/backends/cudo/models.py +37 -0
- dstack/_internal/core/backends/datacrunch/__init__.py +0 -15
- dstack/_internal/core/backends/datacrunch/backend.py +16 -0
- dstack/_internal/core/backends/datacrunch/compute.py +8 -25
- dstack/_internal/core/backends/datacrunch/configurator.py +66 -0
- dstack/_internal/core/backends/datacrunch/models.py +38 -0
- dstack/_internal/core/{models/backends/dstack.py → backends/dstack/models.py} +7 -7
- dstack/_internal/core/backends/gcp/__init__.py +0 -16
- dstack/_internal/core/backends/gcp/auth.py +2 -11
- dstack/_internal/core/backends/gcp/backend.py +17 -0
- dstack/_internal/core/backends/gcp/compute.py +13 -43
- dstack/_internal/{server/services/backends/configurators/gcp.py → core/backends/gcp/configurator.py} +46 -103
- dstack/_internal/core/backends/gcp/models.py +125 -0
- dstack/_internal/core/backends/kubernetes/__init__.py +0 -15
- dstack/_internal/core/backends/kubernetes/backend.py +16 -0
- dstack/_internal/core/backends/kubernetes/compute.py +16 -5
- dstack/_internal/core/backends/kubernetes/configurator.py +55 -0
- dstack/_internal/core/backends/kubernetes/models.py +72 -0
- dstack/_internal/core/backends/lambdalabs/__init__.py +0 -16
- dstack/_internal/core/backends/lambdalabs/backend.py +17 -0
- dstack/_internal/core/backends/lambdalabs/compute.py +7 -28
- dstack/_internal/core/backends/lambdalabs/configurator.py +82 -0
- dstack/_internal/core/backends/lambdalabs/models.py +37 -0
- dstack/_internal/core/backends/local/__init__.py +0 -13
- dstack/_internal/core/backends/local/backend.py +14 -0
- dstack/_internal/core/backends/local/compute.py +16 -2
- dstack/_internal/core/backends/models.py +128 -0
- dstack/_internal/core/backends/oci/__init__.py +0 -15
- dstack/_internal/core/backends/oci/auth.py +1 -5
- dstack/_internal/core/backends/oci/backend.py +16 -0
- dstack/_internal/core/backends/oci/compute.py +9 -23
- dstack/_internal/{server/services/backends/configurators/oci.py → core/backends/oci/configurator.py} +40 -85
- dstack/_internal/core/{models/backends/oci.py → backends/oci/models.py} +24 -25
- dstack/_internal/core/backends/oci/region.py +1 -1
- dstack/_internal/core/backends/runpod/__init__.py +0 -15
- dstack/_internal/core/backends/runpod/backend.py +16 -0
- dstack/_internal/core/backends/runpod/compute.py +7 -3
- dstack/_internal/core/backends/runpod/configurator.py +59 -0
- dstack/_internal/core/backends/runpod/models.py +54 -0
- dstack/_internal/core/backends/template/__init__.py +0 -0
- dstack/_internal/core/backends/tensordock/__init__.py +0 -15
- dstack/_internal/core/backends/tensordock/backend.py +16 -0
- dstack/_internal/core/backends/tensordock/compute.py +8 -27
- dstack/_internal/core/backends/tensordock/configurator.py +68 -0
- dstack/_internal/core/backends/tensordock/models.py +38 -0
- dstack/_internal/core/backends/vastai/__init__.py +0 -15
- dstack/_internal/core/backends/vastai/backend.py +16 -0
- dstack/_internal/core/backends/vastai/compute.py +2 -2
- dstack/_internal/core/backends/vastai/configurator.py +66 -0
- dstack/_internal/core/backends/vastai/models.py +37 -0
- dstack/_internal/core/backends/vultr/__init__.py +0 -15
- dstack/_internal/core/backends/vultr/backend.py +16 -0
- dstack/_internal/core/backends/vultr/compute.py +10 -24
- dstack/_internal/core/backends/vultr/configurator.py +64 -0
- dstack/_internal/core/backends/vultr/models.py +34 -0
- dstack/_internal/core/models/backends/__init__.py +0 -184
- dstack/_internal/core/models/backends/base.py +0 -19
- dstack/_internal/core/models/configurations.py +20 -15
- dstack/_internal/core/models/envs.py +4 -3
- dstack/_internal/core/models/fleets.py +17 -22
- dstack/_internal/core/models/gateways.py +3 -3
- dstack/_internal/core/models/instances.py +24 -0
- dstack/_internal/core/models/profiles.py +41 -46
- dstack/_internal/core/models/projects.py +1 -1
- dstack/_internal/core/models/repos/base.py +0 -5
- dstack/_internal/core/models/repos/local.py +3 -3
- dstack/_internal/core/models/repos/remote.py +26 -12
- dstack/_internal/core/models/repos/virtual.py +1 -1
- dstack/_internal/core/models/resources.py +45 -76
- dstack/_internal/core/models/runs.py +17 -19
- dstack/_internal/core/models/volumes.py +1 -3
- dstack/_internal/core/services/profiles.py +7 -16
- dstack/_internal/core/services/repos.py +0 -4
- dstack/_internal/server/app.py +0 -3
- dstack/_internal/server/background/tasks/process_gateways.py +4 -8
- dstack/_internal/server/background/tasks/process_instances.py +14 -9
- dstack/_internal/server/background/tasks/process_metrics.py +1 -1
- dstack/_internal/server/background/tasks/process_placement_groups.py +4 -1
- dstack/_internal/server/background/tasks/process_prometheus_metrics.py +1 -1
- dstack/_internal/server/background/tasks/process_running_jobs.py +14 -5
- dstack/_internal/server/background/tasks/process_submitted_jobs.py +16 -37
- dstack/_internal/server/background/tasks/process_volumes.py +5 -2
- dstack/_internal/server/migrations/versions/7bc2586e8b9e_make_instancemodel_pool_id_optional.py +36 -0
- dstack/_internal/server/migrations/versions/bc8ca4a505c6_store_backendtype_as_string.py +171 -0
- dstack/_internal/server/models.py +48 -9
- dstack/_internal/server/routers/backends.py +14 -23
- dstack/_internal/server/routers/instances.py +3 -4
- dstack/_internal/server/routers/metrics.py +10 -8
- dstack/_internal/server/routers/prometheus.py +1 -1
- dstack/_internal/server/routers/repos.py +1 -2
- dstack/_internal/server/routers/runs.py +13 -59
- dstack/_internal/server/schemas/gateways.py +14 -23
- dstack/_internal/server/schemas/projects.py +7 -2
- dstack/_internal/server/schemas/repos.py +2 -38
- dstack/_internal/server/schemas/runner.py +1 -0
- dstack/_internal/server/schemas/runs.py +1 -24
- dstack/_internal/server/services/backends/__init__.py +85 -158
- dstack/_internal/server/services/config.py +52 -576
- dstack/_internal/server/services/fleets.py +8 -103
- dstack/_internal/server/services/gateways/__init__.py +12 -4
- dstack/_internal/server/services/{pools.py → instances.py} +22 -329
- dstack/_internal/server/services/jobs/__init__.py +9 -6
- dstack/_internal/server/services/jobs/configurators/base.py +16 -0
- dstack/_internal/server/services/jobs/configurators/dev.py +9 -1
- dstack/_internal/server/services/jobs/configurators/extensions/cursor.py +42 -0
- dstack/_internal/server/services/metrics.py +39 -13
- dstack/_internal/server/services/offers.py +1 -1
- dstack/_internal/server/services/projects.py +23 -14
- dstack/_internal/server/services/prometheus.py +176 -18
- dstack/_internal/server/services/runs.py +24 -16
- dstack/_internal/server/services/volumes.py +8 -4
- dstack/_internal/server/statics/index.html +1 -1
- dstack/_internal/server/statics/{main-4eb116b97819badd1e2c.js → main-4fd5a4770eff59325ee3.js} +7 -7
- dstack/_internal/server/statics/{main-4eb116b97819badd1e2c.js.map → main-4fd5a4770eff59325ee3.js.map} +1 -1
- dstack/_internal/server/testing/common.py +58 -32
- dstack/_internal/utils/json_schema.py +6 -0
- dstack/_internal/utils/ssh.py +2 -1
- dstack/api/__init__.py +4 -0
- dstack/api/_public/__init__.py +16 -20
- dstack/api/_public/backends.py +1 -1
- dstack/api/_public/repos.py +36 -36
- dstack/api/_public/runs.py +167 -83
- dstack/api/server/__init__.py +11 -13
- dstack/api/server/_backends.py +12 -16
- dstack/api/server/_fleets.py +15 -57
- dstack/api/server/_gateways.py +3 -14
- dstack/api/server/_repos.py +1 -4
- dstack/api/server/_runs.py +21 -100
- dstack/api/server/_volumes.py +10 -5
- dstack/version.py +1 -1
- {dstack-0.18.44.dist-info → dstack-0.19.0rc1.dist-info}/METADATA +1 -1
- {dstack-0.18.44.dist-info → dstack-0.19.0rc1.dist-info}/RECORD +218 -204
- tests/_internal/cli/services/configurators/test_profile.py +6 -6
- tests/_internal/core/backends/aws/test_configurator.py +35 -0
- tests/_internal/core/backends/aws/test_resources.py +1 -1
- tests/_internal/core/backends/azure/test_configurator.py +61 -0
- tests/_internal/core/backends/cudo/__init__.py +0 -0
- tests/_internal/core/backends/cudo/test_configurator.py +37 -0
- tests/_internal/core/backends/datacrunch/__init__.py +0 -0
- tests/_internal/core/backends/datacrunch/test_configurator.py +17 -0
- tests/_internal/core/backends/gcp/test_configurator.py +42 -0
- tests/_internal/core/backends/kubernetes/test_configurator.py +43 -0
- tests/_internal/core/backends/lambdalabs/__init__.py +0 -0
- tests/_internal/core/backends/lambdalabs/test_configurator.py +38 -0
- tests/_internal/core/backends/oci/test_configurator.py +55 -0
- tests/_internal/core/backends/runpod/__init__.py +0 -0
- tests/_internal/core/backends/runpod/test_configurator.py +33 -0
- tests/_internal/core/backends/tensordock/__init__.py +0 -0
- tests/_internal/core/backends/tensordock/test_configurator.py +38 -0
- tests/_internal/core/backends/vastai/__init__.py +0 -0
- tests/_internal/core/backends/vastai/test_configurator.py +33 -0
- tests/_internal/core/backends/vultr/__init__.py +0 -0
- tests/_internal/core/backends/vultr/test_configurator.py +33 -0
- tests/_internal/server/background/tasks/test_process_gateways.py +4 -0
- tests/_internal/server/background/tasks/test_process_instances.py +49 -48
- tests/_internal/server/background/tasks/test_process_metrics.py +0 -3
- tests/_internal/server/background/tasks/test_process_placement_groups.py +2 -0
- tests/_internal/server/background/tasks/test_process_prometheus_metrics.py +0 -3
- tests/_internal/server/background/tasks/test_process_running_jobs.py +0 -21
- tests/_internal/server/background/tasks/test_process_runs.py +8 -22
- tests/_internal/server/background/tasks/test_process_submitted_jobs.py +3 -40
- tests/_internal/server/background/tasks/test_process_submitted_volumes.py +2 -0
- tests/_internal/server/background/tasks/test_process_terminating_jobs.py +10 -15
- tests/_internal/server/routers/test_backends.py +6 -764
- tests/_internal/server/routers/test_fleets.py +0 -26
- tests/_internal/server/routers/test_gateways.py +27 -3
- tests/_internal/server/routers/test_instances.py +0 -10
- tests/_internal/server/routers/test_metrics.py +27 -0
- tests/_internal/server/routers/test_projects.py +56 -0
- tests/_internal/server/routers/test_prometheus.py +116 -27
- tests/_internal/server/routers/test_repos.py +0 -15
- tests/_internal/server/routers/test_runs.py +4 -219
- tests/_internal/server/routers/test_volumes.py +2 -3
- tests/_internal/server/services/backends/__init__.py +0 -0
- tests/_internal/server/services/jobs/configurators/test_task.py +35 -0
- tests/_internal/server/services/test_config.py +7 -4
- tests/_internal/server/services/test_fleets.py +1 -4
- tests/_internal/server/services/{test_pools.py → test_instances.py} +11 -49
- tests/_internal/server/services/test_metrics.py +9 -5
- tests/_internal/server/services/test_repos.py +1 -14
- tests/_internal/server/services/test_runs.py +0 -4
- dstack/_internal/cli/commands/pool.py +0 -581
- dstack/_internal/cli/commands/run.py +0 -75
- dstack/_internal/core/backends/aws/config.py +0 -18
- dstack/_internal/core/backends/azure/config.py +0 -12
- dstack/_internal/core/backends/base/config.py +0 -5
- dstack/_internal/core/backends/cudo/config.py +0 -9
- dstack/_internal/core/backends/datacrunch/config.py +0 -9
- dstack/_internal/core/backends/gcp/config.py +0 -22
- dstack/_internal/core/backends/kubernetes/config.py +0 -6
- dstack/_internal/core/backends/lambdalabs/config.py +0 -9
- dstack/_internal/core/backends/nebius/__init__.py +0 -15
- dstack/_internal/core/backends/nebius/api_client.py +0 -319
- dstack/_internal/core/backends/nebius/compute.py +0 -220
- dstack/_internal/core/backends/nebius/config.py +0 -6
- dstack/_internal/core/backends/nebius/types.py +0 -37
- dstack/_internal/core/backends/oci/config.py +0 -6
- dstack/_internal/core/backends/runpod/config.py +0 -17
- dstack/_internal/core/backends/tensordock/config.py +0 -9
- dstack/_internal/core/backends/vastai/config.py +0 -6
- dstack/_internal/core/backends/vultr/config.py +0 -9
- dstack/_internal/core/models/backends/aws.py +0 -86
- dstack/_internal/core/models/backends/azure.py +0 -68
- dstack/_internal/core/models/backends/cudo.py +0 -43
- dstack/_internal/core/models/backends/datacrunch.py +0 -44
- dstack/_internal/core/models/backends/gcp.py +0 -67
- dstack/_internal/core/models/backends/kubernetes.py +0 -40
- dstack/_internal/core/models/backends/lambdalabs.py +0 -43
- dstack/_internal/core/models/backends/nebius.py +0 -54
- dstack/_internal/core/models/backends/runpod.py +0 -42
- dstack/_internal/core/models/backends/tensordock.py +0 -44
- dstack/_internal/core/models/backends/vastai.py +0 -43
- dstack/_internal/core/models/backends/vultr.py +0 -40
- dstack/_internal/core/models/pools.py +0 -43
- dstack/_internal/server/routers/pools.py +0 -142
- dstack/_internal/server/schemas/pools.py +0 -38
- dstack/_internal/server/services/backends/configurators/base.py +0 -72
- dstack/_internal/server/services/backends/configurators/cudo.py +0 -87
- dstack/_internal/server/services/backends/configurators/datacrunch.py +0 -79
- dstack/_internal/server/services/backends/configurators/kubernetes.py +0 -63
- dstack/_internal/server/services/backends/configurators/lambdalabs.py +0 -98
- dstack/_internal/server/services/backends/configurators/nebius.py +0 -85
- dstack/_internal/server/services/backends/configurators/runpod.py +0 -67
- dstack/_internal/server/services/backends/configurators/tensordock.py +0 -82
- dstack/_internal/server/services/backends/configurators/vastai.py +0 -80
- dstack/_internal/server/services/backends/configurators/vultr.py +0 -80
- dstack/api/_public/pools.py +0 -41
- dstack/api/_public/resources.py +0 -105
- dstack/api/server/_pools.py +0 -63
- tests/_internal/server/routers/test_pools.py +0 -612
- /dstack/_internal/{server/services/backends/configurators → core/backends/dstack}/__init__.py +0 -0
- {dstack-0.18.44.dist-info → dstack-0.19.0rc1.dist-info}/LICENSE.md +0 -0
- {dstack-0.18.44.dist-info → dstack-0.19.0rc1.dist-info}/WHEEL +0 -0
- {dstack-0.18.44.dist-info → dstack-0.19.0rc1.dist-info}/entry_points.txt +0 -0
- {dstack-0.18.44.dist-info → dstack-0.19.0rc1.dist-info}/top_level.txt +0 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import enum
|
|
1
2
|
import uuid
|
|
2
3
|
from datetime import datetime
|
|
3
4
|
from typing import Callable, List, Optional, Union
|
|
@@ -29,7 +30,7 @@ from dstack._internal.core.models.fleets import FleetStatus
|
|
|
29
30
|
from dstack._internal.core.models.gateways import GatewayStatus
|
|
30
31
|
from dstack._internal.core.models.instances import InstanceStatus
|
|
31
32
|
from dstack._internal.core.models.profiles import (
|
|
32
|
-
|
|
33
|
+
DEFAULT_FLEET_TERMINATION_IDLE_TIME,
|
|
33
34
|
TerminationPolicy,
|
|
34
35
|
)
|
|
35
36
|
from dstack._internal.core.models.repos.base import RepoType
|
|
@@ -112,7 +113,11 @@ class EncryptedString(TypeDecorator):
|
|
|
112
113
|
cls._encrypt_func = encrypt_func
|
|
113
114
|
cls._decrypt_func = decrypt_func
|
|
114
115
|
|
|
115
|
-
def process_bind_param(
|
|
116
|
+
def process_bind_param(
|
|
117
|
+
self, value: Optional[Union[DecryptedString, str]], dialect
|
|
118
|
+
) -> Optional[str]:
|
|
119
|
+
if value is None:
|
|
120
|
+
return None
|
|
116
121
|
if isinstance(value, str):
|
|
117
122
|
# Passing string allows binding an encrypted value directly
|
|
118
123
|
# e.g. for comparisons
|
|
@@ -130,6 +135,29 @@ class EncryptedString(TypeDecorator):
|
|
|
130
135
|
return DecryptedString(plaintext=None, decrypted=False, exc=e)
|
|
131
136
|
|
|
132
137
|
|
|
138
|
+
class EnumAsString(TypeDecorator):
|
|
139
|
+
"""
|
|
140
|
+
A custom type decorator that stores enums as strings in the DB.
|
|
141
|
+
"""
|
|
142
|
+
|
|
143
|
+
impl = String
|
|
144
|
+
cache_ok = True
|
|
145
|
+
|
|
146
|
+
def __init__(self, enum_class: type[enum.Enum], *args, **kwargs):
|
|
147
|
+
self.enum_class = enum_class
|
|
148
|
+
super().__init__(*args, **kwargs)
|
|
149
|
+
|
|
150
|
+
def process_bind_param(self, value: Optional[enum.Enum], dialect) -> Optional[str]:
|
|
151
|
+
if value is None:
|
|
152
|
+
return None
|
|
153
|
+
return value.name
|
|
154
|
+
|
|
155
|
+
def process_result_value(self, value: Optional[str], dialect) -> Optional[enum.Enum]:
|
|
156
|
+
if value is None:
|
|
157
|
+
return None
|
|
158
|
+
return self.enum_class[value]
|
|
159
|
+
|
|
160
|
+
|
|
133
161
|
constraint_naming_convention = {
|
|
134
162
|
"ix": "ix_%(column_0_label)s",
|
|
135
163
|
"uq": "uq_%(table_name)s_%(column_0_name)s",
|
|
@@ -193,8 +221,13 @@ class ProjectModel(BaseModel):
|
|
|
193
221
|
foreign_keys=[default_gateway_id]
|
|
194
222
|
)
|
|
195
223
|
|
|
224
|
+
# TODO: Drop after the release without pools
|
|
225
|
+
# Note that multi-replica deployments can break if
|
|
226
|
+
# upgrading from an old version that uses pools to the version that drops pools from the DB.
|
|
196
227
|
default_pool_id: Mapped[Optional[UUIDType]] = mapped_column(
|
|
197
|
-
ForeignKey("pools.id", use_alter=True, ondelete="SET NULL"),
|
|
228
|
+
ForeignKey("pools.id", use_alter=True, ondelete="SET NULL"),
|
|
229
|
+
nullable=True,
|
|
230
|
+
deferred=True, # Not loaded so it can be deleted in the next releases
|
|
198
231
|
)
|
|
199
232
|
default_pool: Mapped[Optional["PoolModel"]] = relationship(foreign_keys=[default_pool_id])
|
|
200
233
|
|
|
@@ -222,7 +255,7 @@ class BackendModel(BaseModel):
|
|
|
222
255
|
)
|
|
223
256
|
project_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("projects.id", ondelete="CASCADE"))
|
|
224
257
|
project: Mapped["ProjectModel"] = relationship()
|
|
225
|
-
type: Mapped[BackendType] = mapped_column(
|
|
258
|
+
type: Mapped[BackendType] = mapped_column(EnumAsString(BackendType, 100))
|
|
226
259
|
|
|
227
260
|
config: Mapped[str] = mapped_column(String(20000))
|
|
228
261
|
auth: Mapped[DecryptedString] = mapped_column(EncryptedString(20000))
|
|
@@ -428,6 +461,7 @@ class GatewayComputeModel(BaseModel):
|
|
|
428
461
|
app_updated_at: Mapped[datetime] = mapped_column(NaiveDateTime, default=get_current_datetime)
|
|
429
462
|
|
|
430
463
|
|
|
464
|
+
# TODO: Drop after the release without pools
|
|
431
465
|
class PoolModel(BaseModel):
|
|
432
466
|
__tablename__ = "pools"
|
|
433
467
|
|
|
@@ -493,8 +527,12 @@ class InstanceModel(BaseModel):
|
|
|
493
527
|
project_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("projects.id", ondelete="CASCADE"))
|
|
494
528
|
project: Mapped["ProjectModel"] = relationship(foreign_keys=[project_id])
|
|
495
529
|
|
|
496
|
-
|
|
497
|
-
|
|
530
|
+
# TODO: Drop after the release without pools
|
|
531
|
+
pool_id: Mapped[Optional[uuid.UUID]] = mapped_column(
|
|
532
|
+
ForeignKey("pools.id"),
|
|
533
|
+
deferred=True, # Not loaded so it can be deleted in the next releases
|
|
534
|
+
)
|
|
535
|
+
pool: Mapped[Optional["PoolModel"]] = relationship(back_populates="instances")
|
|
498
536
|
|
|
499
537
|
fleet_id: Mapped[Optional[uuid.UUID]] = mapped_column(ForeignKey("fleets.id"))
|
|
500
538
|
fleet: Mapped[Optional["FleetModel"]] = relationship(back_populates="instances")
|
|
@@ -517,9 +555,10 @@ class InstanceModel(BaseModel):
|
|
|
517
555
|
|
|
518
556
|
# temination policy
|
|
519
557
|
termination_policy: Mapped[Optional[TerminationPolicy]] = mapped_column(String(100))
|
|
520
|
-
# TODO: Suggestion: do not assign
|
|
558
|
+
# TODO: Suggestion: do not assign DEFAULT_FLEET_TERMINATION_IDLE_TIME as the default here
|
|
559
|
+
# (make Optional instead; also instead of -1)
|
|
521
560
|
termination_idle_time: Mapped[int] = mapped_column(
|
|
522
|
-
Integer, default=
|
|
561
|
+
Integer, default=DEFAULT_FLEET_TERMINATION_IDLE_TIME
|
|
523
562
|
)
|
|
524
563
|
|
|
525
564
|
# retry policy
|
|
@@ -533,7 +572,7 @@ class InstanceModel(BaseModel):
|
|
|
533
572
|
last_termination_retry_at: Mapped[Optional[datetime]] = mapped_column(NaiveDateTime)
|
|
534
573
|
|
|
535
574
|
# backend
|
|
536
|
-
backend: Mapped[Optional[BackendType]] = mapped_column(
|
|
575
|
+
backend: Mapped[Optional[BackendType]] = mapped_column(EnumAsString(BackendType, 100))
|
|
537
576
|
backend_data: Mapped[Optional[str]] = mapped_column(Text)
|
|
538
577
|
|
|
539
578
|
# offer
|
|
@@ -3,13 +3,12 @@ from typing import List, Tuple
|
|
|
3
3
|
from fastapi import APIRouter, Depends
|
|
4
4
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
from dstack._internal.core.models
|
|
8
|
-
|
|
9
|
-
AnyConfigInfoWithCredsPartial,
|
|
10
|
-
AnyConfigValues,
|
|
6
|
+
import dstack._internal.core.backends.configurators
|
|
7
|
+
from dstack._internal.core.backends.models import (
|
|
8
|
+
AnyBackendConfigWithCreds,
|
|
11
9
|
BackendInfoYAML,
|
|
12
10
|
)
|
|
11
|
+
from dstack._internal.core.errors import ResourceNotExistsError
|
|
13
12
|
from dstack._internal.core.models.backends.base import BackendType
|
|
14
13
|
from dstack._internal.server import settings
|
|
15
14
|
from dstack._internal.server.db import get_session
|
|
@@ -19,7 +18,7 @@ from dstack._internal.server.schemas.backends import (
|
|
|
19
18
|
DeleteBackendsRequest,
|
|
20
19
|
UpdateBackendYAMLRequest,
|
|
21
20
|
)
|
|
22
|
-
from dstack._internal.server.security.permissions import
|
|
21
|
+
from dstack._internal.server.security.permissions import ProjectAdmin
|
|
23
22
|
from dstack._internal.server.services import backends
|
|
24
23
|
from dstack._internal.server.services.backends import handlers as backends_handlers
|
|
25
24
|
from dstack._internal.server.services.config import (
|
|
@@ -44,23 +43,15 @@ project_router = APIRouter(
|
|
|
44
43
|
|
|
45
44
|
@root_router.post("/list_types")
|
|
46
45
|
async def list_backend_types() -> List[BackendType]:
|
|
47
|
-
return backends.list_available_backend_types()
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
@root_router.post("/config_values")
|
|
51
|
-
async def get_backend_config_values(
|
|
52
|
-
body: AnyConfigInfoWithCredsPartial,
|
|
53
|
-
user: UserModel = Depends(Authenticated()),
|
|
54
|
-
) -> AnyConfigValues:
|
|
55
|
-
return await backends.get_backend_config_values(config=body)
|
|
46
|
+
return dstack._internal.core.backends.configurators.list_available_backend_types()
|
|
56
47
|
|
|
57
48
|
|
|
58
49
|
@project_router.post("/create")
|
|
59
50
|
async def create_backend(
|
|
60
|
-
body:
|
|
51
|
+
body: AnyBackendConfigWithCreds,
|
|
61
52
|
session: AsyncSession = Depends(get_session),
|
|
62
53
|
user_project: Tuple[UserModel, ProjectModel] = Depends(ProjectAdmin()),
|
|
63
|
-
) ->
|
|
54
|
+
) -> AnyBackendConfigWithCreds:
|
|
64
55
|
_, project = user_project
|
|
65
56
|
config = await backends.create_backend(session=session, project=project, config=body)
|
|
66
57
|
if settings.SERVER_CONFIG_ENABLED:
|
|
@@ -70,10 +61,10 @@ async def create_backend(
|
|
|
70
61
|
|
|
71
62
|
@project_router.post("/update")
|
|
72
63
|
async def update_backend(
|
|
73
|
-
body:
|
|
64
|
+
body: AnyBackendConfigWithCreds,
|
|
74
65
|
session: AsyncSession = Depends(get_session),
|
|
75
66
|
user_project: Tuple[UserModel, ProjectModel] = Depends(ProjectAdmin()),
|
|
76
|
-
) ->
|
|
67
|
+
) -> AnyBackendConfigWithCreds:
|
|
77
68
|
_, project = user_project
|
|
78
69
|
config = await backends.update_backend(session=session, project=project, config=body)
|
|
79
70
|
if settings.SERVER_CONFIG_ENABLED:
|
|
@@ -99,12 +90,12 @@ async def delete_backends(
|
|
|
99
90
|
async def get_backend_config_info(
|
|
100
91
|
backend_name: BackendType,
|
|
101
92
|
user_project: Tuple[UserModel, ProjectModel] = Depends(ProjectAdmin()),
|
|
102
|
-
) ->
|
|
93
|
+
) -> AnyBackendConfigWithCreds:
|
|
103
94
|
_, project = user_project
|
|
104
|
-
|
|
105
|
-
if
|
|
95
|
+
config = await backends.get_backend_config(project=project, backend_type=backend_name)
|
|
96
|
+
if config is None:
|
|
106
97
|
raise ResourceNotExistsError()
|
|
107
|
-
return
|
|
98
|
+
return config
|
|
108
99
|
|
|
109
100
|
|
|
110
101
|
@project_router.post("/create_yaml")
|
|
@@ -3,8 +3,8 @@ from typing import List
|
|
|
3
3
|
from fastapi import APIRouter, Depends
|
|
4
4
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
5
5
|
|
|
6
|
-
import dstack._internal.server.services.
|
|
7
|
-
from dstack._internal.core.models.
|
|
6
|
+
import dstack._internal.server.services.instances as instances
|
|
7
|
+
from dstack._internal.core.models.instances import Instance
|
|
8
8
|
from dstack._internal.server.db import get_session
|
|
9
9
|
from dstack._internal.server.models import UserModel
|
|
10
10
|
from dstack._internal.server.schemas.instances import ListInstancesRequest
|
|
@@ -31,12 +31,11 @@ async def list_instances(
|
|
|
31
31
|
The results are paginated. To get the next page, pass `created_at` and `id` of
|
|
32
32
|
the last instance from the previous page as `prev_created_at` and `prev_id`.
|
|
33
33
|
"""
|
|
34
|
-
return await
|
|
34
|
+
return await instances.list_user_instances(
|
|
35
35
|
session=session,
|
|
36
36
|
user=user,
|
|
37
37
|
project_names=body.project_names,
|
|
38
38
|
fleet_ids=body.fleet_ids,
|
|
39
|
-
pool_name=None,
|
|
40
39
|
only_active=body.only_active,
|
|
41
40
|
prev_created_at=body.prev_created_at,
|
|
42
41
|
prev_id=body.prev_id,
|
|
@@ -40,14 +40,16 @@ async def get_job_metrics(
|
|
|
40
40
|
By default, returns one latest sample. To control time window/number of samples, use
|
|
41
41
|
`limit`, `after`, `before`.
|
|
42
42
|
|
|
43
|
-
Supported metrics:
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
43
|
+
Supported metrics (all optional):
|
|
44
|
+
* `cpus_detected_num`
|
|
45
|
+
* `cpu_usage_percent`
|
|
46
|
+
* `memory_total_bytes`
|
|
47
|
+
* `memory_usage_bytes`
|
|
48
|
+
* `memory_working_set_bytes`
|
|
49
|
+
* `gpus_detected_num`
|
|
50
|
+
* `gpu_memory_total_bytes`
|
|
51
|
+
* `gpu_memory_usage_bytes_gpu{i}`
|
|
52
|
+
* `gpu_util_percent_gpu{i}`
|
|
51
53
|
"""
|
|
52
54
|
_, project = user_project
|
|
53
55
|
|
|
@@ -26,7 +26,7 @@ async def get_prometheus_metrics(
|
|
|
26
26
|
return await prometheus.get_metrics(session=session)
|
|
27
27
|
|
|
28
28
|
|
|
29
|
-
@router.get("/metrics/project/{project_name}")
|
|
29
|
+
@router.get("/metrics/project/{project_name}", deprecated=True)
|
|
30
30
|
async def get_project_prometheus_metrics(
|
|
31
31
|
session: Annotated[AsyncSession, Depends(get_session)],
|
|
32
32
|
project: Annotated[ProjectModel, Depends(Project())],
|
|
@@ -66,14 +66,13 @@ async def init_repo(
|
|
|
66
66
|
You can create `virtual` repos if you don't use git repos.
|
|
67
67
|
"""
|
|
68
68
|
user, project = user_project
|
|
69
|
-
repo_creds = body.repo_creds.to_remote_repo_creds(body.repo_info) if body.repo_creds else None
|
|
70
69
|
await repos.init_repo(
|
|
71
70
|
session=session,
|
|
72
71
|
project=project,
|
|
73
72
|
user=user,
|
|
74
73
|
repo_id=body.repo_id,
|
|
75
74
|
repo_info=body.repo_info,
|
|
76
|
-
repo_creds=repo_creds,
|
|
75
|
+
repo_creds=body.repo_creds,
|
|
77
76
|
)
|
|
78
77
|
|
|
79
78
|
|
|
@@ -3,16 +3,13 @@ from typing import List, Tuple
|
|
|
3
3
|
from fastapi import APIRouter, Depends
|
|
4
4
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
5
5
|
|
|
6
|
-
from dstack._internal.core.errors import
|
|
7
|
-
from dstack._internal.core.models.
|
|
8
|
-
from dstack._internal.core.models.runs import PoolInstanceOffers, Run, RunPlan
|
|
6
|
+
from dstack._internal.core.errors import ResourceNotExistsError
|
|
7
|
+
from dstack._internal.core.models.runs import Run, RunPlan
|
|
9
8
|
from dstack._internal.server.db import get_session
|
|
10
9
|
from dstack._internal.server.models import ProjectModel, UserModel
|
|
11
10
|
from dstack._internal.server.schemas.runs import (
|
|
12
11
|
ApplyRunPlanRequest,
|
|
13
|
-
CreateInstanceRequest,
|
|
14
12
|
DeleteRunsRequest,
|
|
15
|
-
GetOffersRequest,
|
|
16
13
|
GetRunPlanRequest,
|
|
17
14
|
GetRunRequest,
|
|
18
15
|
ListRunsRequest,
|
|
@@ -20,10 +17,7 @@ from dstack._internal.server.schemas.runs import (
|
|
|
20
17
|
SubmitRunRequest,
|
|
21
18
|
)
|
|
22
19
|
from dstack._internal.server.security.permissions import Authenticated, ProjectMember
|
|
23
|
-
from dstack._internal.server.services import
|
|
24
|
-
from dstack._internal.server.services.pools import (
|
|
25
|
-
get_or_create_pool_by_name,
|
|
26
|
-
)
|
|
20
|
+
from dstack._internal.server.services import runs
|
|
27
21
|
from dstack._internal.server.utils.routers import get_base_api_additional_responses
|
|
28
22
|
|
|
29
23
|
root_router = APIRouter(
|
|
@@ -132,23 +126,6 @@ async def apply_plan(
|
|
|
132
126
|
)
|
|
133
127
|
|
|
134
128
|
|
|
135
|
-
# apply_plan replaces submit_run since it can create new runs.
|
|
136
|
-
# submit_run can be deprecated in the future.
|
|
137
|
-
@project_router.post("/submit")
|
|
138
|
-
async def submit_run(
|
|
139
|
-
body: SubmitRunRequest,
|
|
140
|
-
session: AsyncSession = Depends(get_session),
|
|
141
|
-
user_project: Tuple[UserModel, ProjectModel] = Depends(ProjectMember()),
|
|
142
|
-
) -> Run:
|
|
143
|
-
user, project = user_project
|
|
144
|
-
return await runs.submit_run(
|
|
145
|
-
session=session,
|
|
146
|
-
user=user,
|
|
147
|
-
project=project,
|
|
148
|
-
run_spec=body.run_spec,
|
|
149
|
-
)
|
|
150
|
-
|
|
151
|
-
|
|
152
129
|
@project_router.post("/stop")
|
|
153
130
|
async def stop_runs(
|
|
154
131
|
body: StopRunsRequest,
|
|
@@ -180,40 +157,17 @@ async def delete_runs(
|
|
|
180
157
|
await runs.delete_runs(session=session, project=project, runs_names=body.runs_names)
|
|
181
158
|
|
|
182
159
|
|
|
183
|
-
#
|
|
184
|
-
@project_router.post("/
|
|
185
|
-
async def
|
|
186
|
-
body:
|
|
160
|
+
# apply_plan replaces submit_run since it can create new runs.
|
|
161
|
+
@project_router.post("/submit", deprecated=True)
|
|
162
|
+
async def submit_run(
|
|
163
|
+
body: SubmitRunRequest,
|
|
187
164
|
session: AsyncSession = Depends(get_session),
|
|
188
165
|
user_project: Tuple[UserModel, ProjectModel] = Depends(ProjectMember()),
|
|
189
|
-
) ->
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
166
|
+
) -> Run:
|
|
167
|
+
user, project = user_project
|
|
168
|
+
return await runs.submit_run(
|
|
169
|
+
session=session,
|
|
170
|
+
user=user,
|
|
193
171
|
project=project,
|
|
194
|
-
|
|
195
|
-
requirements=body.requirements,
|
|
172
|
+
run_spec=body.run_spec,
|
|
196
173
|
)
|
|
197
|
-
instances = [instance for _, instance in offers]
|
|
198
|
-
return PoolInstanceOffers(pool_name=pool.name, instances=instances)
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
# FIXME: get_offers and create_instance semantically belong to pools, not runs
|
|
202
|
-
@project_router.post("/create_instance", deprecated=True)
|
|
203
|
-
async def create_instance(
|
|
204
|
-
body: CreateInstanceRequest,
|
|
205
|
-
session: AsyncSession = Depends(get_session),
|
|
206
|
-
user_project: Tuple[UserModel, ProjectModel] = Depends(ProjectMember()),
|
|
207
|
-
) -> Instance:
|
|
208
|
-
user, project = user_project
|
|
209
|
-
try:
|
|
210
|
-
instance = await fleets.create_instance(
|
|
211
|
-
session=session,
|
|
212
|
-
project=project,
|
|
213
|
-
user=user,
|
|
214
|
-
profile=body.profile,
|
|
215
|
-
requirements=body.requirements,
|
|
216
|
-
)
|
|
217
|
-
except ComputeError as e:
|
|
218
|
-
raise ServerClientError(str(e))
|
|
219
|
-
return instance
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
from typing import Dict, List, Optional
|
|
1
|
+
from typing import Annotated, Any, Dict, List, Optional
|
|
2
2
|
|
|
3
|
-
from pydantic import
|
|
3
|
+
from pydantic import Field
|
|
4
4
|
|
|
5
5
|
from dstack._internal.core.models.backends.base import BackendType
|
|
6
6
|
from dstack._internal.core.models.common import CoreModel
|
|
@@ -8,27 +8,18 @@ from dstack._internal.core.models.gateways import GatewayConfiguration
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
class CreateGatewayRequest(CoreModel):
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
raise ValueError("backend_type must be specified")
|
|
24
|
-
if region is None:
|
|
25
|
-
raise ValueError("region must be specified")
|
|
26
|
-
values["configuration"] = GatewayConfiguration(
|
|
27
|
-
name=values.get("name", None),
|
|
28
|
-
backend=backend_type,
|
|
29
|
-
region=region,
|
|
30
|
-
)
|
|
31
|
-
return values
|
|
11
|
+
configuration: GatewayConfiguration
|
|
12
|
+
# Deprecated and unused. Left for compatibility with 0.18 clients.
|
|
13
|
+
name: Annotated[Optional[str], Field(exclude=True)] = None
|
|
14
|
+
backend_type: Annotated[Optional[BackendType], Field(exclude=True)] = None
|
|
15
|
+
region: Annotated[Optional[str], Field(exclude=True)] = None
|
|
16
|
+
|
|
17
|
+
class Config:
|
|
18
|
+
@staticmethod
|
|
19
|
+
def schema_extra(schema: Dict[str, Any]) -> None:
|
|
20
|
+
del schema["properties"]["name"]
|
|
21
|
+
del schema["properties"]["backend_type"]
|
|
22
|
+
del schema["properties"]["region"]
|
|
32
23
|
|
|
33
24
|
|
|
34
25
|
class GetGatewayRequest(CoreModel):
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
from typing import List
|
|
1
|
+
from typing import Annotated, List
|
|
2
|
+
|
|
3
|
+
from pydantic import Field
|
|
2
4
|
|
|
3
5
|
from dstack._internal.core.models.common import CoreModel
|
|
4
6
|
from dstack._internal.core.models.users import ProjectRole
|
|
@@ -13,7 +15,10 @@ class DeleteProjectsRequest(CoreModel):
|
|
|
13
15
|
|
|
14
16
|
|
|
15
17
|
class MemberSetting(CoreModel):
|
|
16
|
-
username:
|
|
18
|
+
username: Annotated[
|
|
19
|
+
str,
|
|
20
|
+
Field(description="The username or email of the user"),
|
|
21
|
+
]
|
|
17
22
|
project_role: ProjectRole
|
|
18
23
|
|
|
19
24
|
|
|
@@ -4,46 +4,10 @@ from pydantic import Field
|
|
|
4
4
|
|
|
5
5
|
from dstack._internal.core.models.common import CoreModel
|
|
6
6
|
from dstack._internal.core.models.repos import AnyRepoInfo
|
|
7
|
-
from dstack._internal.core.models.repos.
|
|
8
|
-
from dstack._internal.core.models.repos.remote import RemoteRepoCreds, RemoteRepoInfo
|
|
7
|
+
from dstack._internal.core.models.repos.remote import RemoteRepoCreds
|
|
9
8
|
from dstack._internal.server.schemas.common import RepoRequest
|
|
10
9
|
|
|
11
10
|
|
|
12
|
-
# TODO: in 0.19, either remove this model or make clone_url required
|
|
13
|
-
class RemoteRepoCredsDto(CoreModel):
|
|
14
|
-
protocol: RepoProtocol
|
|
15
|
-
clone_url: Optional[str]
|
|
16
|
-
private_key: Optional[str]
|
|
17
|
-
oauth_token: Optional[str]
|
|
18
|
-
|
|
19
|
-
@staticmethod
|
|
20
|
-
def from_remote_repo_creds(creds: RemoteRepoCreds) -> "RemoteRepoCredsDto":
|
|
21
|
-
return RemoteRepoCredsDto(
|
|
22
|
-
protocol=creds.protocol,
|
|
23
|
-
clone_url=creds.clone_url,
|
|
24
|
-
private_key=creds.private_key,
|
|
25
|
-
oauth_token=creds.oauth_token,
|
|
26
|
-
)
|
|
27
|
-
|
|
28
|
-
def to_remote_repo_creds(self, info: RemoteRepoInfo) -> RemoteRepoCreds:
|
|
29
|
-
if (clone_url := self.clone_url) is None:
|
|
30
|
-
netloc = (
|
|
31
|
-
f"{info.repo_host_name}:{info.repo_port}"
|
|
32
|
-
if info.repo_port
|
|
33
|
-
else info.repo_host_name
|
|
34
|
-
)
|
|
35
|
-
if self.protocol == RepoProtocol.SSH:
|
|
36
|
-
clone_url = f"ssh://git@{netloc}/{info.repo_user_name}/{info.repo_name}.git"
|
|
37
|
-
else:
|
|
38
|
-
clone_url = f"https://{netloc}/{info.repo_user_name}/{info.repo_name}.git"
|
|
39
|
-
return RemoteRepoCreds(
|
|
40
|
-
protocol=self.protocol,
|
|
41
|
-
clone_url=clone_url,
|
|
42
|
-
private_key=self.private_key,
|
|
43
|
-
oauth_token=self.oauth_token,
|
|
44
|
-
)
|
|
45
|
-
|
|
46
|
-
|
|
47
11
|
class GetRepoRequest(RepoRequest):
|
|
48
12
|
include_creds: bool
|
|
49
13
|
|
|
@@ -51,7 +15,7 @@ class GetRepoRequest(RepoRequest):
|
|
|
51
15
|
class SaveRepoCredsRequest(RepoRequest):
|
|
52
16
|
repo_info: AnyRepoInfo
|
|
53
17
|
repo_creds: Annotated[
|
|
54
|
-
Optional[
|
|
18
|
+
Optional[RemoteRepoCreds],
|
|
55
19
|
Field(description="The repo creds for accessing private remote repo"),
|
|
56
20
|
]
|
|
57
21
|
|
|
@@ -5,9 +5,7 @@ from uuid import UUID
|
|
|
5
5
|
from pydantic import Field
|
|
6
6
|
|
|
7
7
|
from dstack._internal.core.models.common import CoreModel
|
|
8
|
-
from dstack._internal.core.models.
|
|
9
|
-
from dstack._internal.core.models.profiles import Profile
|
|
10
|
-
from dstack._internal.core.models.runs import ApplyRunPlanInput, Requirements, RunSpec
|
|
8
|
+
from dstack._internal.core.models.runs import ApplyRunPlanInput, RunSpec
|
|
11
9
|
|
|
12
10
|
|
|
13
11
|
class ListRunsRequest(CoreModel):
|
|
@@ -30,27 +28,6 @@ class GetRunPlanRequest(CoreModel):
|
|
|
30
28
|
run_spec: RunSpec
|
|
31
29
|
|
|
32
30
|
|
|
33
|
-
class GetOffersRequest(CoreModel):
|
|
34
|
-
profile: Profile
|
|
35
|
-
requirements: Requirements
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
class CreateInstanceRequest(CoreModel):
|
|
39
|
-
profile: Profile
|
|
40
|
-
requirements: Requirements
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
class AddRemoteInstanceRequest(CoreModel):
|
|
44
|
-
pool_name: Optional[str]
|
|
45
|
-
instance_name: Optional[str]
|
|
46
|
-
instance_network: Optional[str]
|
|
47
|
-
region: Optional[str]
|
|
48
|
-
host: str
|
|
49
|
-
port: int
|
|
50
|
-
ssh_user: str
|
|
51
|
-
ssh_keys: List[SSHKey]
|
|
52
|
-
|
|
53
|
-
|
|
54
31
|
class SubmitRunRequest(CoreModel):
|
|
55
32
|
run_spec: RunSpec
|
|
56
33
|
|