dstack 0.18.44__py3-none-any.whl → 0.19.0__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.

Potentially problematic release.


This version of dstack might be problematic. Click here for more details.

Files changed (267) hide show
  1. dstack/_internal/cli/commands/gateway.py +15 -3
  2. dstack/_internal/cli/commands/logs.py +0 -22
  3. dstack/_internal/cli/commands/stats.py +8 -17
  4. dstack/_internal/cli/main.py +1 -5
  5. dstack/_internal/cli/services/configurators/fleet.py +4 -39
  6. dstack/_internal/cli/services/configurators/run.py +22 -21
  7. dstack/_internal/cli/services/profile.py +34 -83
  8. dstack/_internal/cli/utils/gateway.py +1 -1
  9. dstack/_internal/core/backends/__init__.py +56 -39
  10. dstack/_internal/core/backends/aws/__init__.py +0 -25
  11. dstack/_internal/core/backends/aws/auth.py +1 -10
  12. dstack/_internal/core/backends/aws/backend.py +26 -0
  13. dstack/_internal/core/backends/aws/compute.py +20 -45
  14. dstack/_internal/{server/services/backends/configurators/aws.py → core/backends/aws/configurator.py} +46 -85
  15. dstack/_internal/core/backends/aws/models.py +135 -0
  16. dstack/_internal/core/backends/aws/resources.py +1 -1
  17. dstack/_internal/core/backends/azure/__init__.py +0 -20
  18. dstack/_internal/core/backends/azure/auth.py +2 -11
  19. dstack/_internal/core/backends/azure/backend.py +21 -0
  20. dstack/_internal/core/backends/azure/compute.py +13 -27
  21. dstack/_internal/{server/services/backends/configurators/azure.py → core/backends/azure/configurator.py} +141 -210
  22. dstack/_internal/core/backends/azure/models.py +89 -0
  23. dstack/_internal/core/backends/base/__init__.py +0 -12
  24. dstack/_internal/core/backends/base/backend.py +18 -0
  25. dstack/_internal/core/backends/base/compute.py +153 -33
  26. dstack/_internal/core/backends/base/configurator.py +105 -0
  27. dstack/_internal/core/backends/base/models.py +14 -0
  28. dstack/_internal/core/backends/configurators.py +138 -0
  29. dstack/_internal/core/backends/cudo/__init__.py +0 -15
  30. dstack/_internal/core/backends/cudo/backend.py +16 -0
  31. dstack/_internal/core/backends/cudo/compute.py +8 -26
  32. dstack/_internal/core/backends/cudo/configurator.py +72 -0
  33. dstack/_internal/core/backends/cudo/models.py +37 -0
  34. dstack/_internal/core/backends/datacrunch/__init__.py +0 -15
  35. dstack/_internal/core/backends/datacrunch/backend.py +16 -0
  36. dstack/_internal/core/backends/datacrunch/compute.py +8 -25
  37. dstack/_internal/core/backends/datacrunch/configurator.py +66 -0
  38. dstack/_internal/core/backends/datacrunch/models.py +38 -0
  39. dstack/_internal/core/{models/backends/dstack.py → backends/dstack/models.py} +7 -7
  40. dstack/_internal/core/backends/gcp/__init__.py +0 -16
  41. dstack/_internal/core/backends/gcp/auth.py +2 -11
  42. dstack/_internal/core/backends/gcp/backend.py +17 -0
  43. dstack/_internal/core/backends/gcp/compute.py +13 -43
  44. dstack/_internal/{server/services/backends/configurators/gcp.py → core/backends/gcp/configurator.py} +46 -103
  45. dstack/_internal/core/backends/gcp/models.py +125 -0
  46. dstack/_internal/core/backends/kubernetes/__init__.py +0 -15
  47. dstack/_internal/core/backends/kubernetes/backend.py +16 -0
  48. dstack/_internal/core/backends/kubernetes/compute.py +16 -5
  49. dstack/_internal/core/backends/kubernetes/configurator.py +55 -0
  50. dstack/_internal/core/backends/kubernetes/models.py +72 -0
  51. dstack/_internal/core/backends/lambdalabs/__init__.py +0 -16
  52. dstack/_internal/core/backends/lambdalabs/backend.py +17 -0
  53. dstack/_internal/core/backends/lambdalabs/compute.py +7 -28
  54. dstack/_internal/core/backends/lambdalabs/configurator.py +82 -0
  55. dstack/_internal/core/backends/lambdalabs/models.py +37 -0
  56. dstack/_internal/core/backends/local/__init__.py +0 -13
  57. dstack/_internal/core/backends/local/backend.py +14 -0
  58. dstack/_internal/core/backends/local/compute.py +16 -2
  59. dstack/_internal/core/backends/models.py +128 -0
  60. dstack/_internal/core/backends/oci/__init__.py +0 -15
  61. dstack/_internal/core/backends/oci/auth.py +1 -5
  62. dstack/_internal/core/backends/oci/backend.py +16 -0
  63. dstack/_internal/core/backends/oci/compute.py +9 -23
  64. dstack/_internal/{server/services/backends/configurators/oci.py → core/backends/oci/configurator.py} +40 -85
  65. dstack/_internal/core/{models/backends/oci.py → backends/oci/models.py} +24 -25
  66. dstack/_internal/core/backends/oci/region.py +1 -1
  67. dstack/_internal/core/backends/runpod/__init__.py +0 -15
  68. dstack/_internal/core/backends/runpod/backend.py +16 -0
  69. dstack/_internal/core/backends/runpod/compute.py +7 -3
  70. dstack/_internal/core/backends/runpod/configurator.py +59 -0
  71. dstack/_internal/core/backends/runpod/models.py +54 -0
  72. dstack/_internal/core/backends/template/__init__.py +0 -0
  73. dstack/_internal/core/backends/tensordock/__init__.py +0 -15
  74. dstack/_internal/core/backends/tensordock/backend.py +16 -0
  75. dstack/_internal/core/backends/tensordock/compute.py +8 -27
  76. dstack/_internal/core/backends/tensordock/configurator.py +68 -0
  77. dstack/_internal/core/backends/tensordock/models.py +38 -0
  78. dstack/_internal/core/backends/vastai/__init__.py +0 -15
  79. dstack/_internal/core/backends/vastai/backend.py +16 -0
  80. dstack/_internal/core/backends/vastai/compute.py +2 -2
  81. dstack/_internal/core/backends/vastai/configurator.py +66 -0
  82. dstack/_internal/core/backends/vastai/models.py +37 -0
  83. dstack/_internal/core/backends/vultr/__init__.py +0 -15
  84. dstack/_internal/core/backends/vultr/backend.py +16 -0
  85. dstack/_internal/core/backends/vultr/compute.py +10 -24
  86. dstack/_internal/core/backends/vultr/configurator.py +64 -0
  87. dstack/_internal/core/backends/vultr/models.py +34 -0
  88. dstack/_internal/core/models/backends/__init__.py +0 -184
  89. dstack/_internal/core/models/backends/base.py +0 -19
  90. dstack/_internal/core/models/configurations.py +20 -15
  91. dstack/_internal/core/models/envs.py +4 -3
  92. dstack/_internal/core/models/fleets.py +17 -22
  93. dstack/_internal/core/models/gateways.py +3 -3
  94. dstack/_internal/core/models/instances.py +24 -0
  95. dstack/_internal/core/models/profiles.py +41 -46
  96. dstack/_internal/core/models/projects.py +1 -1
  97. dstack/_internal/core/models/repos/base.py +0 -5
  98. dstack/_internal/core/models/repos/local.py +3 -3
  99. dstack/_internal/core/models/repos/remote.py +26 -12
  100. dstack/_internal/core/models/repos/virtual.py +1 -1
  101. dstack/_internal/core/models/resources.py +45 -76
  102. dstack/_internal/core/models/runs.py +17 -19
  103. dstack/_internal/core/models/volumes.py +1 -3
  104. dstack/_internal/core/services/profiles.py +7 -16
  105. dstack/_internal/core/services/repos.py +0 -4
  106. dstack/_internal/server/app.py +0 -3
  107. dstack/_internal/server/background/tasks/process_gateways.py +4 -8
  108. dstack/_internal/server/background/tasks/process_instances.py +14 -9
  109. dstack/_internal/server/background/tasks/process_metrics.py +1 -1
  110. dstack/_internal/server/background/tasks/process_placement_groups.py +4 -1
  111. dstack/_internal/server/background/tasks/process_prometheus_metrics.py +1 -1
  112. dstack/_internal/server/background/tasks/process_running_jobs.py +14 -5
  113. dstack/_internal/server/background/tasks/process_submitted_jobs.py +16 -37
  114. dstack/_internal/server/background/tasks/process_volumes.py +5 -2
  115. dstack/_internal/server/migrations/versions/7bc2586e8b9e_make_instancemodel_pool_id_optional.py +36 -0
  116. dstack/_internal/server/migrations/versions/bc8ca4a505c6_store_backendtype_as_string.py +171 -0
  117. dstack/_internal/server/models.py +48 -9
  118. dstack/_internal/server/routers/backends.py +14 -23
  119. dstack/_internal/server/routers/instances.py +3 -4
  120. dstack/_internal/server/routers/metrics.py +10 -8
  121. dstack/_internal/server/routers/prometheus.py +1 -1
  122. dstack/_internal/server/routers/repos.py +1 -2
  123. dstack/_internal/server/routers/runs.py +13 -59
  124. dstack/_internal/server/schemas/gateways.py +14 -23
  125. dstack/_internal/server/schemas/projects.py +7 -2
  126. dstack/_internal/server/schemas/repos.py +2 -38
  127. dstack/_internal/server/schemas/runner.py +1 -0
  128. dstack/_internal/server/schemas/runs.py +1 -24
  129. dstack/_internal/server/services/backends/__init__.py +85 -158
  130. dstack/_internal/server/services/config.py +52 -576
  131. dstack/_internal/server/services/fleets.py +8 -103
  132. dstack/_internal/server/services/gateways/__init__.py +12 -4
  133. dstack/_internal/server/services/{pools.py → instances.py} +22 -329
  134. dstack/_internal/server/services/jobs/__init__.py +9 -6
  135. dstack/_internal/server/services/jobs/configurators/base.py +16 -0
  136. dstack/_internal/server/services/jobs/configurators/dev.py +9 -1
  137. dstack/_internal/server/services/jobs/configurators/extensions/cursor.py +42 -0
  138. dstack/_internal/server/services/metrics.py +39 -13
  139. dstack/_internal/server/services/offers.py +1 -1
  140. dstack/_internal/server/services/projects.py +23 -14
  141. dstack/_internal/server/services/prometheus.py +176 -18
  142. dstack/_internal/server/services/runs.py +24 -16
  143. dstack/_internal/server/services/volumes.py +8 -4
  144. dstack/_internal/server/statics/index.html +1 -1
  145. dstack/_internal/server/statics/{main-4eb116b97819badd1e2c.js → main-4a0fe83e84574654e397.js} +18 -14
  146. dstack/_internal/server/statics/{main-4eb116b97819badd1e2c.js.map → main-4a0fe83e84574654e397.js.map} +1 -1
  147. dstack/_internal/server/testing/common.py +58 -32
  148. dstack/_internal/utils/json_schema.py +6 -0
  149. dstack/_internal/utils/ssh.py +2 -1
  150. dstack/api/__init__.py +4 -0
  151. dstack/api/_public/__init__.py +16 -20
  152. dstack/api/_public/backends.py +1 -1
  153. dstack/api/_public/repos.py +36 -36
  154. dstack/api/_public/runs.py +167 -83
  155. dstack/api/server/__init__.py +11 -13
  156. dstack/api/server/_backends.py +12 -16
  157. dstack/api/server/_fleets.py +15 -57
  158. dstack/api/server/_gateways.py +3 -14
  159. dstack/api/server/_repos.py +1 -4
  160. dstack/api/server/_runs.py +21 -100
  161. dstack/api/server/_volumes.py +10 -5
  162. dstack/version.py +1 -1
  163. {dstack-0.18.44.dist-info → dstack-0.19.0.dist-info}/METADATA +1 -1
  164. {dstack-0.18.44.dist-info → dstack-0.19.0.dist-info}/RECORD +218 -204
  165. tests/_internal/cli/services/configurators/test_profile.py +6 -6
  166. tests/_internal/core/backends/aws/test_configurator.py +35 -0
  167. tests/_internal/core/backends/aws/test_resources.py +1 -1
  168. tests/_internal/core/backends/azure/test_configurator.py +61 -0
  169. tests/_internal/core/backends/cudo/__init__.py +0 -0
  170. tests/_internal/core/backends/cudo/test_configurator.py +37 -0
  171. tests/_internal/core/backends/datacrunch/__init__.py +0 -0
  172. tests/_internal/core/backends/datacrunch/test_configurator.py +17 -0
  173. tests/_internal/core/backends/gcp/test_configurator.py +42 -0
  174. tests/_internal/core/backends/kubernetes/test_configurator.py +43 -0
  175. tests/_internal/core/backends/lambdalabs/__init__.py +0 -0
  176. tests/_internal/core/backends/lambdalabs/test_configurator.py +38 -0
  177. tests/_internal/core/backends/oci/test_configurator.py +55 -0
  178. tests/_internal/core/backends/runpod/__init__.py +0 -0
  179. tests/_internal/core/backends/runpod/test_configurator.py +33 -0
  180. tests/_internal/core/backends/tensordock/__init__.py +0 -0
  181. tests/_internal/core/backends/tensordock/test_configurator.py +38 -0
  182. tests/_internal/core/backends/vastai/__init__.py +0 -0
  183. tests/_internal/core/backends/vastai/test_configurator.py +33 -0
  184. tests/_internal/core/backends/vultr/__init__.py +0 -0
  185. tests/_internal/core/backends/vultr/test_configurator.py +33 -0
  186. tests/_internal/server/background/tasks/test_process_gateways.py +4 -0
  187. tests/_internal/server/background/tasks/test_process_instances.py +49 -48
  188. tests/_internal/server/background/tasks/test_process_metrics.py +0 -3
  189. tests/_internal/server/background/tasks/test_process_placement_groups.py +2 -0
  190. tests/_internal/server/background/tasks/test_process_prometheus_metrics.py +0 -3
  191. tests/_internal/server/background/tasks/test_process_running_jobs.py +0 -21
  192. tests/_internal/server/background/tasks/test_process_runs.py +8 -22
  193. tests/_internal/server/background/tasks/test_process_submitted_jobs.py +3 -40
  194. tests/_internal/server/background/tasks/test_process_submitted_volumes.py +2 -0
  195. tests/_internal/server/background/tasks/test_process_terminating_jobs.py +10 -15
  196. tests/_internal/server/routers/test_backends.py +6 -764
  197. tests/_internal/server/routers/test_fleets.py +0 -26
  198. tests/_internal/server/routers/test_gateways.py +27 -3
  199. tests/_internal/server/routers/test_instances.py +0 -10
  200. tests/_internal/server/routers/test_metrics.py +27 -0
  201. tests/_internal/server/routers/test_projects.py +56 -0
  202. tests/_internal/server/routers/test_prometheus.py +116 -27
  203. tests/_internal/server/routers/test_repos.py +0 -15
  204. tests/_internal/server/routers/test_runs.py +4 -219
  205. tests/_internal/server/routers/test_volumes.py +2 -3
  206. tests/_internal/server/services/backends/__init__.py +0 -0
  207. tests/_internal/server/services/jobs/configurators/test_task.py +35 -0
  208. tests/_internal/server/services/test_config.py +7 -4
  209. tests/_internal/server/services/test_fleets.py +1 -4
  210. tests/_internal/server/services/{test_pools.py → test_instances.py} +11 -49
  211. tests/_internal/server/services/test_metrics.py +9 -5
  212. tests/_internal/server/services/test_repos.py +1 -14
  213. tests/_internal/server/services/test_runs.py +0 -4
  214. dstack/_internal/cli/commands/pool.py +0 -581
  215. dstack/_internal/cli/commands/run.py +0 -75
  216. dstack/_internal/core/backends/aws/config.py +0 -18
  217. dstack/_internal/core/backends/azure/config.py +0 -12
  218. dstack/_internal/core/backends/base/config.py +0 -5
  219. dstack/_internal/core/backends/cudo/config.py +0 -9
  220. dstack/_internal/core/backends/datacrunch/config.py +0 -9
  221. dstack/_internal/core/backends/gcp/config.py +0 -22
  222. dstack/_internal/core/backends/kubernetes/config.py +0 -6
  223. dstack/_internal/core/backends/lambdalabs/config.py +0 -9
  224. dstack/_internal/core/backends/nebius/__init__.py +0 -15
  225. dstack/_internal/core/backends/nebius/api_client.py +0 -319
  226. dstack/_internal/core/backends/nebius/compute.py +0 -220
  227. dstack/_internal/core/backends/nebius/config.py +0 -6
  228. dstack/_internal/core/backends/nebius/types.py +0 -37
  229. dstack/_internal/core/backends/oci/config.py +0 -6
  230. dstack/_internal/core/backends/runpod/config.py +0 -17
  231. dstack/_internal/core/backends/tensordock/config.py +0 -9
  232. dstack/_internal/core/backends/vastai/config.py +0 -6
  233. dstack/_internal/core/backends/vultr/config.py +0 -9
  234. dstack/_internal/core/models/backends/aws.py +0 -86
  235. dstack/_internal/core/models/backends/azure.py +0 -68
  236. dstack/_internal/core/models/backends/cudo.py +0 -43
  237. dstack/_internal/core/models/backends/datacrunch.py +0 -44
  238. dstack/_internal/core/models/backends/gcp.py +0 -67
  239. dstack/_internal/core/models/backends/kubernetes.py +0 -40
  240. dstack/_internal/core/models/backends/lambdalabs.py +0 -43
  241. dstack/_internal/core/models/backends/nebius.py +0 -54
  242. dstack/_internal/core/models/backends/runpod.py +0 -42
  243. dstack/_internal/core/models/backends/tensordock.py +0 -44
  244. dstack/_internal/core/models/backends/vastai.py +0 -43
  245. dstack/_internal/core/models/backends/vultr.py +0 -40
  246. dstack/_internal/core/models/pools.py +0 -43
  247. dstack/_internal/server/routers/pools.py +0 -142
  248. dstack/_internal/server/schemas/pools.py +0 -38
  249. dstack/_internal/server/services/backends/configurators/base.py +0 -72
  250. dstack/_internal/server/services/backends/configurators/cudo.py +0 -87
  251. dstack/_internal/server/services/backends/configurators/datacrunch.py +0 -79
  252. dstack/_internal/server/services/backends/configurators/kubernetes.py +0 -63
  253. dstack/_internal/server/services/backends/configurators/lambdalabs.py +0 -98
  254. dstack/_internal/server/services/backends/configurators/nebius.py +0 -85
  255. dstack/_internal/server/services/backends/configurators/runpod.py +0 -67
  256. dstack/_internal/server/services/backends/configurators/tensordock.py +0 -82
  257. dstack/_internal/server/services/backends/configurators/vastai.py +0 -80
  258. dstack/_internal/server/services/backends/configurators/vultr.py +0 -80
  259. dstack/api/_public/pools.py +0 -41
  260. dstack/api/_public/resources.py +0 -105
  261. dstack/api/server/_pools.py +0 -63
  262. tests/_internal/server/routers/test_pools.py +0 -612
  263. /dstack/_internal/{server/services/backends/configurators → core/backends/dstack}/__init__.py +0 -0
  264. {dstack-0.18.44.dist-info → dstack-0.19.0.dist-info}/LICENSE.md +0 -0
  265. {dstack-0.18.44.dist-info → dstack-0.19.0.dist-info}/WHEEL +0 -0
  266. {dstack-0.18.44.dist-info → dstack-0.19.0.dist-info}/entry_points.txt +0 -0
  267. {dstack-0.18.44.dist-info → dstack-0.19.0.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
- DEFAULT_POOL_TERMINATION_IDLE_TIME,
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(self, value: Union[DecryptedString, str], dialect):
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"), nullable=True
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(Enum(BackendType))
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
- pool_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("pools.id"))
497
- pool: Mapped["PoolModel"] = relationship(back_populates="instances")
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 DEFAULT_POOL_TERMINATION_IDLE_TIME as the default here (make Optional instead; also instead of -1)
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=DEFAULT_POOL_TERMINATION_IDLE_TIME
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(Enum(BackendType))
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
- from dstack._internal.core.errors import ResourceNotExistsError
7
- from dstack._internal.core.models.backends import (
8
- AnyConfigInfoWithCreds,
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 Authenticated, ProjectAdmin
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: AnyConfigInfoWithCreds,
51
+ body: AnyBackendConfigWithCreds,
61
52
  session: AsyncSession = Depends(get_session),
62
53
  user_project: Tuple[UserModel, ProjectModel] = Depends(ProjectAdmin()),
63
- ) -> AnyConfigInfoWithCreds:
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: AnyConfigInfoWithCreds,
64
+ body: AnyBackendConfigWithCreds,
74
65
  session: AsyncSession = Depends(get_session),
75
66
  user_project: Tuple[UserModel, ProjectModel] = Depends(ProjectAdmin()),
76
- ) -> AnyConfigInfoWithCreds:
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
- ) -> AnyConfigInfoWithCreds:
93
+ ) -> AnyBackendConfigWithCreds:
103
94
  _, project = user_project
104
- config_info = await backends.get_config_info(project=project, backend_type=backend_name)
105
- if config_info is None:
95
+ config = await backends.get_backend_config(project=project, backend_type=backend_name)
96
+ if config is None:
106
97
  raise ResourceNotExistsError()
107
- return config_info
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.pools as pools
7
- from dstack._internal.core.models.pools import Instance
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 pools.list_user_pool_instances(
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
- "cpu_usage_percent",
45
- "memory_usage_bytes",
46
- "memory_working_set_bytes",
47
- "gpus_detected_num",
48
- "gpu_memory_usage_bytes_gpu{i}",
49
- "gpu_util_percent_gpu{i}"
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 ComputeError, ResourceNotExistsError, ServerClientError
7
- from dstack._internal.core.models.pools import Instance
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 fleets, runs
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
- # FIXME: get_offers and create_instance semantically belong to pools, not runs
184
- @project_router.post("/get_offers", deprecated=True)
185
- async def get_offers(
186
- body: GetOffersRequest,
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
- ) -> PoolInstanceOffers:
190
- _, project = user_project
191
- pool = await get_or_create_pool_by_name(session, project, body.profile.pool_name)
192
- offers = await fleets.get_create_instance_offers(
166
+ ) -> Run:
167
+ user, project = user_project
168
+ return await runs.submit_run(
169
+ session=session,
170
+ user=user,
193
171
  project=project,
194
- profile=body.profile,
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 root_validator
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
- name: Optional[str]
12
- backend_type: Optional[BackendType]
13
- region: Optional[str]
14
- configuration: Optional[GatewayConfiguration]
15
-
16
- @root_validator
17
- def fill_configuration(cls, values: Dict) -> Dict:
18
- if values.get("configuration", None) is not None:
19
- return values
20
- backend_type = values.get("backend_type", None)
21
- region = values.get("region", None)
22
- if backend_type is None:
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: str
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.base import RepoProtocol
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[RemoteRepoCredsDto],
18
+ Optional[RemoteRepoCreds],
55
19
  Field(description="The repo creds for accessing private remote repo"),
56
20
  ]
57
21
 
@@ -64,6 +64,7 @@ class SubmitBody(CoreModel):
64
64
  "gateway",
65
65
  "single_branch",
66
66
  "max_duration",
67
+ "ssh_key",
67
68
  "working_dir",
68
69
  }
69
70
  ),
@@ -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.instances import SSHKey
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