dstack 0.18.43__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.
Files changed (278) 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 -20
  7. dstack/_internal/cli/services/profile.py +34 -83
  8. dstack/_internal/cli/utils/gateway.py +1 -1
  9. dstack/_internal/cli/utils/run.py +11 -0
  10. dstack/_internal/core/backends/__init__.py +56 -39
  11. dstack/_internal/core/backends/aws/__init__.py +0 -25
  12. dstack/_internal/core/backends/aws/auth.py +1 -10
  13. dstack/_internal/core/backends/aws/backend.py +26 -0
  14. dstack/_internal/core/backends/aws/compute.py +21 -45
  15. dstack/_internal/{server/services/backends/configurators/aws.py → core/backends/aws/configurator.py} +46 -85
  16. dstack/_internal/core/backends/aws/models.py +135 -0
  17. dstack/_internal/core/backends/aws/resources.py +1 -1
  18. dstack/_internal/core/backends/azure/__init__.py +0 -20
  19. dstack/_internal/core/backends/azure/auth.py +2 -11
  20. dstack/_internal/core/backends/azure/backend.py +21 -0
  21. dstack/_internal/core/backends/azure/compute.py +14 -28
  22. dstack/_internal/{server/services/backends/configurators/azure.py → core/backends/azure/configurator.py} +141 -210
  23. dstack/_internal/core/backends/azure/models.py +89 -0
  24. dstack/_internal/core/backends/base/__init__.py +0 -12
  25. dstack/_internal/core/backends/base/backend.py +18 -0
  26. dstack/_internal/core/backends/base/compute.py +153 -33
  27. dstack/_internal/core/backends/base/configurator.py +105 -0
  28. dstack/_internal/core/backends/base/models.py +14 -0
  29. dstack/_internal/core/backends/configurators.py +138 -0
  30. dstack/_internal/core/backends/cudo/__init__.py +0 -15
  31. dstack/_internal/core/backends/cudo/backend.py +16 -0
  32. dstack/_internal/core/backends/cudo/compute.py +8 -26
  33. dstack/_internal/core/backends/cudo/configurator.py +72 -0
  34. dstack/_internal/core/backends/cudo/models.py +37 -0
  35. dstack/_internal/core/backends/datacrunch/__init__.py +0 -15
  36. dstack/_internal/core/backends/datacrunch/backend.py +16 -0
  37. dstack/_internal/core/backends/datacrunch/compute.py +8 -25
  38. dstack/_internal/core/backends/datacrunch/configurator.py +66 -0
  39. dstack/_internal/core/backends/datacrunch/models.py +38 -0
  40. dstack/_internal/core/{models/backends/dstack.py → backends/dstack/models.py} +7 -7
  41. dstack/_internal/core/backends/gcp/__init__.py +0 -16
  42. dstack/_internal/core/backends/gcp/auth.py +2 -11
  43. dstack/_internal/core/backends/gcp/backend.py +17 -0
  44. dstack/_internal/core/backends/gcp/compute.py +14 -44
  45. dstack/_internal/{server/services/backends/configurators/gcp.py → core/backends/gcp/configurator.py} +46 -103
  46. dstack/_internal/core/backends/gcp/models.py +125 -0
  47. dstack/_internal/core/backends/kubernetes/__init__.py +0 -15
  48. dstack/_internal/core/backends/kubernetes/backend.py +16 -0
  49. dstack/_internal/core/backends/kubernetes/compute.py +16 -5
  50. dstack/_internal/core/backends/kubernetes/configurator.py +55 -0
  51. dstack/_internal/core/backends/kubernetes/models.py +72 -0
  52. dstack/_internal/core/backends/lambdalabs/__init__.py +0 -16
  53. dstack/_internal/core/backends/lambdalabs/backend.py +17 -0
  54. dstack/_internal/core/backends/lambdalabs/compute.py +7 -28
  55. dstack/_internal/core/backends/lambdalabs/configurator.py +82 -0
  56. dstack/_internal/core/backends/lambdalabs/models.py +37 -0
  57. dstack/_internal/core/backends/local/__init__.py +0 -13
  58. dstack/_internal/core/backends/local/backend.py +14 -0
  59. dstack/_internal/core/backends/local/compute.py +16 -2
  60. dstack/_internal/core/backends/models.py +128 -0
  61. dstack/_internal/core/backends/oci/__init__.py +0 -15
  62. dstack/_internal/core/backends/oci/auth.py +1 -5
  63. dstack/_internal/core/backends/oci/backend.py +16 -0
  64. dstack/_internal/core/backends/oci/compute.py +9 -23
  65. dstack/_internal/{server/services/backends/configurators/oci.py → core/backends/oci/configurator.py} +40 -85
  66. dstack/_internal/core/{models/backends/oci.py → backends/oci/models.py} +24 -25
  67. dstack/_internal/core/backends/oci/region.py +1 -1
  68. dstack/_internal/core/backends/runpod/__init__.py +0 -15
  69. dstack/_internal/core/backends/runpod/backend.py +16 -0
  70. dstack/_internal/core/backends/runpod/compute.py +28 -6
  71. dstack/_internal/core/backends/runpod/configurator.py +59 -0
  72. dstack/_internal/core/backends/runpod/models.py +54 -0
  73. dstack/_internal/core/backends/template/__init__.py +0 -0
  74. dstack/_internal/core/backends/tensordock/__init__.py +0 -15
  75. dstack/_internal/core/backends/tensordock/backend.py +16 -0
  76. dstack/_internal/core/backends/tensordock/compute.py +8 -27
  77. dstack/_internal/core/backends/tensordock/configurator.py +68 -0
  78. dstack/_internal/core/backends/tensordock/models.py +38 -0
  79. dstack/_internal/core/backends/vastai/__init__.py +0 -15
  80. dstack/_internal/core/backends/vastai/backend.py +16 -0
  81. dstack/_internal/core/backends/vastai/compute.py +2 -2
  82. dstack/_internal/core/backends/vastai/configurator.py +66 -0
  83. dstack/_internal/core/backends/vastai/models.py +37 -0
  84. dstack/_internal/core/backends/vultr/__init__.py +0 -15
  85. dstack/_internal/core/backends/vultr/backend.py +16 -0
  86. dstack/_internal/core/backends/vultr/compute.py +10 -24
  87. dstack/_internal/core/backends/vultr/configurator.py +64 -0
  88. dstack/_internal/core/backends/vultr/models.py +34 -0
  89. dstack/_internal/core/models/backends/__init__.py +0 -184
  90. dstack/_internal/core/models/backends/base.py +0 -19
  91. dstack/_internal/core/models/configurations.py +22 -16
  92. dstack/_internal/core/models/envs.py +4 -3
  93. dstack/_internal/core/models/fleets.py +17 -22
  94. dstack/_internal/core/models/gateways.py +3 -3
  95. dstack/_internal/core/models/instances.py +24 -0
  96. dstack/_internal/core/models/profiles.py +85 -45
  97. dstack/_internal/core/models/projects.py +1 -1
  98. dstack/_internal/core/models/repos/base.py +0 -5
  99. dstack/_internal/core/models/repos/local.py +3 -3
  100. dstack/_internal/core/models/repos/remote.py +26 -12
  101. dstack/_internal/core/models/repos/virtual.py +1 -1
  102. dstack/_internal/core/models/resources.py +45 -76
  103. dstack/_internal/core/models/runs.py +21 -19
  104. dstack/_internal/core/models/volumes.py +1 -3
  105. dstack/_internal/core/services/profiles.py +7 -16
  106. dstack/_internal/core/services/repos.py +0 -4
  107. dstack/_internal/server/app.py +11 -4
  108. dstack/_internal/server/background/__init__.py +10 -0
  109. dstack/_internal/server/background/tasks/process_gateways.py +4 -8
  110. dstack/_internal/server/background/tasks/process_instances.py +14 -9
  111. dstack/_internal/server/background/tasks/process_metrics.py +1 -1
  112. dstack/_internal/server/background/tasks/process_placement_groups.py +5 -1
  113. dstack/_internal/server/background/tasks/process_prometheus_metrics.py +135 -0
  114. dstack/_internal/server/background/tasks/process_running_jobs.py +80 -24
  115. dstack/_internal/server/background/tasks/process_runs.py +1 -0
  116. dstack/_internal/server/background/tasks/process_submitted_jobs.py +20 -38
  117. dstack/_internal/server/background/tasks/process_volumes.py +5 -2
  118. dstack/_internal/server/migrations/versions/60e444118b6d_add_jobprometheusmetrics.py +40 -0
  119. dstack/_internal/server/migrations/versions/7bc2586e8b9e_make_instancemodel_pool_id_optional.py +36 -0
  120. dstack/_internal/server/migrations/versions/98d1b92988bc_add_jobterminationreason_terminated_due_.py +140 -0
  121. dstack/_internal/server/migrations/versions/bc8ca4a505c6_store_backendtype_as_string.py +171 -0
  122. dstack/_internal/server/models.py +59 -9
  123. dstack/_internal/server/routers/backends.py +14 -23
  124. dstack/_internal/server/routers/instances.py +3 -4
  125. dstack/_internal/server/routers/metrics.py +31 -10
  126. dstack/_internal/server/routers/prometheus.py +36 -0
  127. dstack/_internal/server/routers/repos.py +1 -2
  128. dstack/_internal/server/routers/runs.py +13 -59
  129. dstack/_internal/server/schemas/gateways.py +14 -23
  130. dstack/_internal/server/schemas/projects.py +7 -2
  131. dstack/_internal/server/schemas/repos.py +2 -38
  132. dstack/_internal/server/schemas/runner.py +1 -0
  133. dstack/_internal/server/schemas/runs.py +1 -24
  134. dstack/_internal/server/security/permissions.py +1 -1
  135. dstack/_internal/server/services/backends/__init__.py +85 -158
  136. dstack/_internal/server/services/config.py +53 -567
  137. dstack/_internal/server/services/fleets.py +9 -103
  138. dstack/_internal/server/services/gateways/__init__.py +13 -4
  139. dstack/_internal/server/services/{pools.py → instances.py} +22 -329
  140. dstack/_internal/server/services/jobs/__init__.py +9 -6
  141. dstack/_internal/server/services/jobs/configurators/base.py +25 -1
  142. dstack/_internal/server/services/jobs/configurators/dev.py +9 -1
  143. dstack/_internal/server/services/jobs/configurators/extensions/cursor.py +42 -0
  144. dstack/_internal/server/services/metrics.py +131 -72
  145. dstack/_internal/server/services/offers.py +1 -1
  146. dstack/_internal/server/services/projects.py +23 -14
  147. dstack/_internal/server/services/prometheus.py +245 -0
  148. dstack/_internal/server/services/runner/client.py +14 -3
  149. dstack/_internal/server/services/runs.py +67 -31
  150. dstack/_internal/server/services/volumes.py +9 -4
  151. dstack/_internal/server/settings.py +3 -0
  152. dstack/_internal/server/statics/index.html +1 -1
  153. dstack/_internal/server/statics/{main-fe8fd9db55df8d10e648.js → main-4fd5a4770eff59325ee3.js} +68 -15
  154. dstack/_internal/server/statics/{main-fe8fd9db55df8d10e648.js.map → main-4fd5a4770eff59325ee3.js.map} +1 -1
  155. dstack/_internal/server/statics/{main-7510e71dfa9749a4e70e.css → main-da9f8c06a69c20dac23e.css} +1 -1
  156. dstack/_internal/server/statics/static/media/entraID.d65d1f3e9486a8e56d24fc07b3230885.svg +9 -0
  157. dstack/_internal/server/testing/common.py +75 -32
  158. dstack/_internal/utils/json_schema.py +6 -0
  159. dstack/_internal/utils/ssh.py +2 -1
  160. dstack/api/__init__.py +4 -0
  161. dstack/api/_public/__init__.py +16 -20
  162. dstack/api/_public/backends.py +1 -1
  163. dstack/api/_public/repos.py +36 -36
  164. dstack/api/_public/runs.py +170 -83
  165. dstack/api/server/__init__.py +11 -13
  166. dstack/api/server/_backends.py +12 -16
  167. dstack/api/server/_fleets.py +15 -55
  168. dstack/api/server/_gateways.py +3 -14
  169. dstack/api/server/_repos.py +1 -4
  170. dstack/api/server/_runs.py +21 -96
  171. dstack/api/server/_volumes.py +10 -5
  172. dstack/api/utils.py +3 -0
  173. dstack/version.py +1 -1
  174. {dstack-0.18.43.dist-info → dstack-0.19.0rc1.dist-info}/METADATA +10 -1
  175. {dstack-0.18.43.dist-info → dstack-0.19.0rc1.dist-info}/RECORD +229 -206
  176. tests/_internal/cli/services/configurators/test_profile.py +6 -6
  177. tests/_internal/core/backends/aws/test_configurator.py +35 -0
  178. tests/_internal/core/backends/aws/test_resources.py +1 -1
  179. tests/_internal/core/backends/azure/test_configurator.py +61 -0
  180. tests/_internal/core/backends/cudo/__init__.py +0 -0
  181. tests/_internal/core/backends/cudo/test_configurator.py +37 -0
  182. tests/_internal/core/backends/datacrunch/__init__.py +0 -0
  183. tests/_internal/core/backends/datacrunch/test_configurator.py +17 -0
  184. tests/_internal/core/backends/gcp/test_configurator.py +42 -0
  185. tests/_internal/core/backends/kubernetes/test_configurator.py +43 -0
  186. tests/_internal/core/backends/lambdalabs/__init__.py +0 -0
  187. tests/_internal/core/backends/lambdalabs/test_configurator.py +38 -0
  188. tests/_internal/core/backends/oci/test_configurator.py +55 -0
  189. tests/_internal/core/backends/runpod/__init__.py +0 -0
  190. tests/_internal/core/backends/runpod/test_configurator.py +33 -0
  191. tests/_internal/core/backends/tensordock/__init__.py +0 -0
  192. tests/_internal/core/backends/tensordock/test_configurator.py +38 -0
  193. tests/_internal/core/backends/vastai/__init__.py +0 -0
  194. tests/_internal/core/backends/vastai/test_configurator.py +33 -0
  195. tests/_internal/core/backends/vultr/__init__.py +0 -0
  196. tests/_internal/core/backends/vultr/test_configurator.py +33 -0
  197. tests/_internal/server/background/tasks/test_process_gateways.py +4 -0
  198. tests/_internal/server/background/tasks/test_process_instances.py +49 -48
  199. tests/_internal/server/background/tasks/test_process_metrics.py +0 -3
  200. tests/_internal/server/background/tasks/test_process_placement_groups.py +2 -0
  201. tests/_internal/server/background/tasks/test_process_prometheus_metrics.py +186 -0
  202. tests/_internal/server/background/tasks/test_process_running_jobs.py +123 -19
  203. tests/_internal/server/background/tasks/test_process_runs.py +8 -22
  204. tests/_internal/server/background/tasks/test_process_submitted_jobs.py +3 -40
  205. tests/_internal/server/background/tasks/test_process_submitted_volumes.py +2 -0
  206. tests/_internal/server/background/tasks/test_process_terminating_jobs.py +10 -15
  207. tests/_internal/server/routers/test_backends.py +6 -764
  208. tests/_internal/server/routers/test_fleets.py +2 -26
  209. tests/_internal/server/routers/test_gateways.py +27 -3
  210. tests/_internal/server/routers/test_instances.py +0 -10
  211. tests/_internal/server/routers/test_metrics.py +42 -0
  212. tests/_internal/server/routers/test_projects.py +56 -0
  213. tests/_internal/server/routers/test_prometheus.py +333 -0
  214. tests/_internal/server/routers/test_repos.py +0 -15
  215. tests/_internal/server/routers/test_runs.py +83 -275
  216. tests/_internal/server/routers/test_volumes.py +2 -3
  217. tests/_internal/server/services/backends/__init__.py +0 -0
  218. tests/_internal/server/services/jobs/configurators/test_task.py +35 -0
  219. tests/_internal/server/services/test_config.py +7 -4
  220. tests/_internal/server/services/test_fleets.py +1 -4
  221. tests/_internal/server/services/{test_pools.py → test_instances.py} +11 -49
  222. tests/_internal/server/services/test_metrics.py +167 -0
  223. tests/_internal/server/services/test_repos.py +1 -14
  224. tests/_internal/server/services/test_runs.py +0 -4
  225. dstack/_internal/cli/commands/pool.py +0 -581
  226. dstack/_internal/cli/commands/run.py +0 -75
  227. dstack/_internal/core/backends/aws/config.py +0 -18
  228. dstack/_internal/core/backends/azure/config.py +0 -12
  229. dstack/_internal/core/backends/base/config.py +0 -5
  230. dstack/_internal/core/backends/cudo/config.py +0 -9
  231. dstack/_internal/core/backends/datacrunch/config.py +0 -9
  232. dstack/_internal/core/backends/gcp/config.py +0 -22
  233. dstack/_internal/core/backends/kubernetes/config.py +0 -6
  234. dstack/_internal/core/backends/lambdalabs/config.py +0 -9
  235. dstack/_internal/core/backends/nebius/__init__.py +0 -15
  236. dstack/_internal/core/backends/nebius/api_client.py +0 -319
  237. dstack/_internal/core/backends/nebius/compute.py +0 -220
  238. dstack/_internal/core/backends/nebius/config.py +0 -6
  239. dstack/_internal/core/backends/nebius/types.py +0 -37
  240. dstack/_internal/core/backends/oci/config.py +0 -6
  241. dstack/_internal/core/backends/runpod/config.py +0 -9
  242. dstack/_internal/core/backends/tensordock/config.py +0 -9
  243. dstack/_internal/core/backends/vastai/config.py +0 -6
  244. dstack/_internal/core/backends/vultr/config.py +0 -9
  245. dstack/_internal/core/models/backends/aws.py +0 -86
  246. dstack/_internal/core/models/backends/azure.py +0 -68
  247. dstack/_internal/core/models/backends/cudo.py +0 -43
  248. dstack/_internal/core/models/backends/datacrunch.py +0 -44
  249. dstack/_internal/core/models/backends/gcp.py +0 -67
  250. dstack/_internal/core/models/backends/kubernetes.py +0 -40
  251. dstack/_internal/core/models/backends/lambdalabs.py +0 -43
  252. dstack/_internal/core/models/backends/nebius.py +0 -54
  253. dstack/_internal/core/models/backends/runpod.py +0 -40
  254. dstack/_internal/core/models/backends/tensordock.py +0 -44
  255. dstack/_internal/core/models/backends/vastai.py +0 -43
  256. dstack/_internal/core/models/backends/vultr.py +0 -40
  257. dstack/_internal/core/models/pools.py +0 -43
  258. dstack/_internal/server/routers/pools.py +0 -142
  259. dstack/_internal/server/schemas/pools.py +0 -38
  260. dstack/_internal/server/services/backends/configurators/base.py +0 -72
  261. dstack/_internal/server/services/backends/configurators/cudo.py +0 -87
  262. dstack/_internal/server/services/backends/configurators/datacrunch.py +0 -79
  263. dstack/_internal/server/services/backends/configurators/kubernetes.py +0 -63
  264. dstack/_internal/server/services/backends/configurators/lambdalabs.py +0 -98
  265. dstack/_internal/server/services/backends/configurators/nebius.py +0 -85
  266. dstack/_internal/server/services/backends/configurators/runpod.py +0 -97
  267. dstack/_internal/server/services/backends/configurators/tensordock.py +0 -82
  268. dstack/_internal/server/services/backends/configurators/vastai.py +0 -80
  269. dstack/_internal/server/services/backends/configurators/vultr.py +0 -80
  270. dstack/api/_public/pools.py +0 -41
  271. dstack/api/_public/resources.py +0 -105
  272. dstack/api/server/_pools.py +0 -63
  273. tests/_internal/server/routers/test_pools.py +0 -612
  274. /dstack/_internal/{server/services/backends/configurators → core/backends/dstack}/__init__.py +0 -0
  275. {dstack-0.18.43.dist-info → dstack-0.19.0rc1.dist-info}/LICENSE.md +0 -0
  276. {dstack-0.18.43.dist-info → dstack-0.19.0rc1.dist-info}/WHEEL +0 -0
  277. {dstack-0.18.43.dist-info → dstack-0.19.0rc1.dist-info}/entry_points.txt +0 -0
  278. {dstack-0.18.43.dist-info → dstack-0.19.0rc1.dist-info}/top_level.txt +0 -0
@@ -1,15 +0,0 @@
1
- from dstack._internal.core.backends.base import Backend
2
- from dstack._internal.core.backends.vultr.compute import VultrCompute
3
- from dstack._internal.core.backends.vultr.config import VultrConfig
4
- from dstack._internal.core.models.backends.base import BackendType
5
-
6
-
7
- class VultrBackend(Backend):
8
- TYPE: BackendType = BackendType.VULTR
9
-
10
- def __init__(self, config: VultrConfig):
11
- self.config = config
12
- self._compute = VultrCompute(self.config)
13
-
14
- def compute(self) -> VultrCompute:
15
- return self._compute
@@ -0,0 +1,16 @@
1
+ from dstack._internal.core.backends.base.backend import Backend
2
+ from dstack._internal.core.backends.vultr.compute import VultrCompute
3
+ from dstack._internal.core.backends.vultr.models import VultrConfig
4
+ from dstack._internal.core.models.backends.base import BackendType
5
+
6
+
7
+ class VultrBackend(Backend):
8
+ TYPE = BackendType.VULTR
9
+ COMPUTE_CLASS = VultrCompute
10
+
11
+ def __init__(self, config: VultrConfig):
12
+ self.config = config
13
+ self._compute = VultrCompute(self.config)
14
+
15
+ def compute(self) -> VultrCompute:
16
+ return self._compute
@@ -4,15 +4,16 @@ from typing import List, Optional
4
4
 
5
5
  import requests
6
6
 
7
- from dstack._internal.core.backends.base import Compute
7
+ from dstack._internal.core.backends.base.backend import Compute
8
8
  from dstack._internal.core.backends.base.compute import (
9
+ ComputeWithCreateInstanceSupport,
10
+ ComputeWithMultinodeSupport,
9
11
  generate_unique_instance_name,
10
- get_job_instance_name,
11
12
  get_user_data,
12
13
  )
13
14
  from dstack._internal.core.backends.base.offers import get_catalog_offers
14
15
  from dstack._internal.core.backends.vultr.api_client import VultrApiClient
15
- from dstack._internal.core.backends.vultr.config import VultrConfig
16
+ from dstack._internal.core.backends.vultr.models import VultrConfig
16
17
  from dstack._internal.core.errors import BackendError, ProvisioningError
17
18
  from dstack._internal.core.models.backends.base import BackendType
18
19
  from dstack._internal.core.models.instances import (
@@ -20,10 +21,8 @@ from dstack._internal.core.models.instances import (
20
21
  InstanceConfiguration,
21
22
  InstanceOffer,
22
23
  InstanceOfferWithAvailability,
23
- SSHKey,
24
24
  )
25
- from dstack._internal.core.models.runs import Job, JobProvisioningData, Requirements, Run
26
- from dstack._internal.core.models.volumes import Volume
25
+ from dstack._internal.core.models.runs import JobProvisioningData, Requirements
27
26
  from dstack._internal.utils.logging import get_logger
28
27
 
29
28
  logger = get_logger(__name__)
@@ -31,7 +30,11 @@ logger = get_logger(__name__)
31
30
  MAX_INSTANCE_NAME_LEN = 64
32
31
 
33
32
 
34
- class VultrCompute(Compute):
33
+ class VultrCompute(
34
+ ComputeWithCreateInstanceSupport,
35
+ ComputeWithMultinodeSupport,
36
+ Compute,
37
+ ):
35
38
  def __init__(self, config: VultrConfig):
36
39
  super().__init__()
37
40
  self.config = config
@@ -54,23 +57,6 @@ class VultrCompute(Compute):
54
57
  ]
55
58
  return offers
56
59
 
57
- def run_job(
58
- self,
59
- run: Run,
60
- job: Job,
61
- instance_offer: InstanceOfferWithAvailability,
62
- project_ssh_public_key: str,
63
- project_ssh_private_key: str,
64
- volumes: List[Volume],
65
- ) -> JobProvisioningData:
66
- instance_config = InstanceConfiguration(
67
- project_name=run.project_name,
68
- instance_name=get_job_instance_name(run, job),
69
- ssh_keys=[SSHKey(public=project_ssh_public_key.strip())],
70
- user=run.user,
71
- )
72
- return self.create_instance(instance_offer, instance_config)
73
-
74
60
  def create_instance(
75
61
  self, instance_offer: InstanceOfferWithAvailability, instance_config: InstanceConfiguration
76
62
  ) -> JobProvisioningData:
@@ -0,0 +1,64 @@
1
+ import json
2
+
3
+ from dstack._internal.core.backends.base.configurator import (
4
+ BackendRecord,
5
+ Configurator,
6
+ raise_invalid_credentials_error,
7
+ )
8
+ from dstack._internal.core.backends.models import (
9
+ VultrBackendConfigWithCreds,
10
+ )
11
+ from dstack._internal.core.backends.vultr import api_client
12
+ from dstack._internal.core.backends.vultr.backend import VultrBackend
13
+ from dstack._internal.core.backends.vultr.models import (
14
+ VultrBackendConfig,
15
+ VultrConfig,
16
+ VultrCreds,
17
+ VultrStoredConfig,
18
+ )
19
+ from dstack._internal.core.models.backends.base import (
20
+ BackendType,
21
+ )
22
+
23
+ REGIONS = []
24
+
25
+
26
+ class VultrConfigurator(Configurator):
27
+ TYPE = BackendType.VULTR
28
+ BACKEND_CLASS = VultrBackend
29
+
30
+ def validate_config(self, config: VultrBackendConfigWithCreds, default_creds_enabled: bool):
31
+ self._validate_vultr_api_key(config.creds.api_key)
32
+
33
+ def create_backend(
34
+ self, project_name: str, config: VultrBackendConfigWithCreds
35
+ ) -> BackendRecord:
36
+ if config.regions is None:
37
+ config.regions = REGIONS
38
+ return BackendRecord(
39
+ config=VultrStoredConfig(
40
+ **VultrBackendConfig.__response__.parse_obj(config).dict()
41
+ ).json(),
42
+ auth=VultrCreds.parse_obj(config.creds).json(),
43
+ )
44
+
45
+ def get_backend_config(self, record: BackendRecord, include_creds: bool) -> VultrBackendConfig:
46
+ config = self._get_config(record)
47
+ if include_creds:
48
+ return VultrBackendConfigWithCreds.__response__.parse_obj(config)
49
+ return VultrBackendConfig.__response__.parse_obj(config)
50
+
51
+ def get_backend(self, record: BackendRecord) -> VultrBackend:
52
+ config = self._get_config(record)
53
+ return VultrBackend(config=config)
54
+
55
+ def _get_config(self, record: BackendRecord) -> VultrConfig:
56
+ return VultrConfig.__response__(
57
+ **json.loads(record.config),
58
+ creds=VultrCreds.parse_raw(record.auth),
59
+ )
60
+
61
+ def _validate_vultr_api_key(self, api_key: str):
62
+ client = api_client.VultrApiClient(api_key=api_key)
63
+ if not client.validate_api_key():
64
+ raise_invalid_credentials_error(fields=[["creds", "api_key"]])
@@ -0,0 +1,34 @@
1
+ from typing import Annotated, List, Literal, Optional
2
+
3
+ from pydantic import Field
4
+
5
+ from dstack._internal.core.models.common import CoreModel
6
+
7
+
8
+ class VultrAPIKeyCreds(CoreModel):
9
+ type: Annotated[Literal["api_key"], Field(description="The type of credentials")] = "api_key"
10
+ api_key: Annotated[str, Field(description="The API key")]
11
+
12
+
13
+ AnyVultrCreds = VultrAPIKeyCreds
14
+ VultrCreds = AnyVultrCreds
15
+
16
+
17
+ class VultrBackendConfig(CoreModel):
18
+ type: Annotated[Literal["vultr"], Field(description="The type of backend")] = "vultr"
19
+ regions: Annotated[
20
+ Optional[List[str]],
21
+ Field(description="The list of Vultr regions. Omit to use all regions"),
22
+ ] = None
23
+
24
+
25
+ class VultrBackendConfigWithCreds(VultrBackendConfig):
26
+ creds: Annotated[AnyVultrCreds, Field(description="The credentials")]
27
+
28
+
29
+ class VultrStoredConfig(VultrBackendConfig):
30
+ pass
31
+
32
+
33
+ class VultrConfig(VultrStoredConfig):
34
+ creds: AnyVultrCreds
@@ -1,184 +0,0 @@
1
- from typing import Union
2
-
3
- from dstack._internal.core.models.backends.aws import (
4
- AWSConfigInfo,
5
- AWSConfigInfoWithCreds,
6
- AWSConfigInfoWithCredsPartial,
7
- AWSConfigValues,
8
- )
9
- from dstack._internal.core.models.backends.azure import (
10
- AzureConfigInfo,
11
- AzureConfigInfoWithCreds,
12
- AzureConfigInfoWithCredsPartial,
13
- AzureConfigValues,
14
- )
15
- from dstack._internal.core.models.backends.cudo import (
16
- CudoConfigInfo,
17
- CudoConfigInfoWithCreds,
18
- CudoConfigInfoWithCredsPartial,
19
- CudoConfigValues,
20
- )
21
- from dstack._internal.core.models.backends.datacrunch import (
22
- DataCrunchConfigInfo,
23
- DataCrunchConfigInfoWithCreds,
24
- DataCrunchConfigInfoWithCredsPartial,
25
- DataCrunchConfigValues,
26
- )
27
- from dstack._internal.core.models.backends.dstack import (
28
- DstackBaseBackendConfigInfo,
29
- DstackConfigInfo,
30
- DstackConfigValues,
31
- )
32
- from dstack._internal.core.models.backends.gcp import (
33
- GCPConfigInfo,
34
- GCPConfigInfoWithCreds,
35
- GCPConfigInfoWithCredsPartial,
36
- GCPConfigValues,
37
- )
38
- from dstack._internal.core.models.backends.kubernetes import (
39
- KubernetesConfigInfo,
40
- KubernetesConfigInfoWithCreds,
41
- KubernetesConfigInfoWithCredsPartial,
42
- KubernetesConfigValues,
43
- )
44
- from dstack._internal.core.models.backends.lambdalabs import (
45
- LambdaConfigInfo,
46
- LambdaConfigInfoWithCreds,
47
- LambdaConfigInfoWithCredsPartial,
48
- LambdaConfigValues,
49
- )
50
- from dstack._internal.core.models.backends.nebius import (
51
- NebiusConfigInfo,
52
- NebiusConfigInfoWithCreds,
53
- NebiusConfigInfoWithCredsPartial,
54
- NebiusConfigValues,
55
- )
56
- from dstack._internal.core.models.backends.oci import (
57
- OCIConfigInfo,
58
- OCIConfigInfoWithCreds,
59
- OCIConfigInfoWithCredsPartial,
60
- OCIConfigValues,
61
- )
62
- from dstack._internal.core.models.backends.runpod import (
63
- RunpodConfigInfo,
64
- RunpodConfigInfoWithCreds,
65
- RunpodConfigInfoWithCredsPartial,
66
- RunpodConfigValues,
67
- )
68
- from dstack._internal.core.models.backends.tensordock import (
69
- TensorDockConfigInfo,
70
- TensorDockConfigInfoWithCreds,
71
- TensorDockConfigInfoWithCredsPartial,
72
- TensorDockConfigValues,
73
- )
74
- from dstack._internal.core.models.backends.vastai import (
75
- VastAIConfigInfo,
76
- VastAIConfigInfoWithCreds,
77
- VastAIConfigInfoWithCredsPartial,
78
- VastAIConfigValues,
79
- )
80
- from dstack._internal.core.models.backends.vultr import (
81
- VultrConfigInfo,
82
- VultrConfigInfoWithCreds,
83
- VultrConfigInfoWithCredsPartial,
84
- VultrConfigValues,
85
- )
86
- from dstack._internal.core.models.common import CoreModel
87
-
88
- # The following models are the basis of the JSON-based backend API.
89
- # They are also the models used by the Configurator interface.
90
- # The JSON-based backend API is replaced by the YAML-based backend API and not used.
91
- # It's likely to be deprecated and removed.
92
- # Some of the models below like those needed for interactive backend setup could be removed then.
93
- # Still, others are going to stay as Configurator models to keep YAML-based configs and internal configs separated.
94
-
95
- # Backend config returned by the API
96
- AnyConfigInfoWithoutCreds = Union[
97
- AWSConfigInfo,
98
- AzureConfigInfo,
99
- CudoConfigInfo,
100
- DataCrunchConfigInfo,
101
- GCPConfigInfo,
102
- KubernetesConfigInfo,
103
- LambdaConfigInfo,
104
- NebiusConfigInfo,
105
- OCIConfigInfo,
106
- RunpodConfigInfo,
107
- TensorDockConfigInfo,
108
- VastAIConfigInfo,
109
- VultrConfigInfo,
110
- DstackConfigInfo,
111
- DstackBaseBackendConfigInfo,
112
- ]
113
-
114
- # Same as AnyConfigInfoWithoutCreds but also includes creds.
115
- # Used to create/update backend.
116
- # Also returned by the API to project admins so that they can see/update backend creds.
117
- AnyConfigInfoWithCreds = Union[
118
- AWSConfigInfoWithCreds,
119
- AzureConfigInfoWithCreds,
120
- CudoConfigInfoWithCreds,
121
- DataCrunchConfigInfoWithCreds,
122
- GCPConfigInfoWithCreds,
123
- KubernetesConfigInfoWithCreds,
124
- LambdaConfigInfoWithCreds,
125
- NebiusConfigInfoWithCreds,
126
- OCIConfigInfoWithCreds,
127
- RunpodConfigInfoWithCreds,
128
- TensorDockConfigInfoWithCreds,
129
- VastAIConfigInfoWithCreds,
130
- VultrConfigInfoWithCreds,
131
- DstackConfigInfo,
132
- ]
133
-
134
- AnyConfigInfo = Union[AnyConfigInfoWithoutCreds, AnyConfigInfoWithCreds]
135
-
136
- # Same as AnyConfigInfoWithCreds but some fields may be optional.
137
- # Used for interactive setup with validation and suggestions (e.g. via UI).
138
- # If the backend does not need interactive setup, it's the same as AnyConfigInfoWithCreds.
139
- AnyConfigInfoWithCredsPartial = Union[
140
- AWSConfigInfoWithCredsPartial,
141
- AzureConfigInfoWithCredsPartial,
142
- CudoConfigInfoWithCredsPartial,
143
- DataCrunchConfigInfoWithCredsPartial,
144
- GCPConfigInfoWithCredsPartial,
145
- KubernetesConfigInfoWithCredsPartial,
146
- LambdaConfigInfoWithCredsPartial,
147
- NebiusConfigInfoWithCredsPartial,
148
- OCIConfigInfoWithCredsPartial,
149
- RunpodConfigInfoWithCredsPartial,
150
- TensorDockConfigInfoWithCredsPartial,
151
- VastAIConfigInfoWithCredsPartial,
152
- VultrConfigInfoWithCredsPartial,
153
- DstackConfigInfo,
154
- ]
155
-
156
- # Suggestions for unfilled fields used in interactive setup.
157
- AnyConfigValues = Union[
158
- AWSConfigValues,
159
- AzureConfigValues,
160
- CudoConfigValues,
161
- DataCrunchConfigValues,
162
- GCPConfigValues,
163
- KubernetesConfigValues,
164
- LambdaConfigValues,
165
- NebiusConfigValues,
166
- OCIConfigValues,
167
- RunpodConfigValues,
168
- TensorDockConfigValues,
169
- VastAIConfigValues,
170
- VultrConfigValues,
171
- DstackConfigValues,
172
- ]
173
-
174
-
175
- # In case we'll support multiple backends of the same type,
176
- # this adds backend name to backend config.
177
- class BackendInfo(CoreModel):
178
- name: str
179
- config: AnyConfigInfoWithoutCreds
180
-
181
-
182
- class BackendInfoYAML(CoreModel):
183
- name: str
184
- config_yaml: str
@@ -1,7 +1,4 @@
1
1
  import enum
2
- from typing import List, Optional
3
-
4
- from dstack._internal.core.models.common import CoreModel
5
2
 
6
3
 
7
4
  class BackendType(str, enum.Enum):
@@ -32,24 +29,8 @@ class BackendType(str, enum.Enum):
32
29
  LAMBDA = "lambda"
33
30
  LOCAL = "local"
34
31
  REMOTE = "remote" # TODO: replace for LOCAL
35
- NEBIUS = "nebius"
36
32
  OCI = "oci"
37
33
  RUNPOD = "runpod"
38
34
  TENSORDOCK = "tensordock"
39
35
  VASTAI = "vastai"
40
36
  VULTR = "vultr"
41
-
42
-
43
- class ConfigElementValue(CoreModel):
44
- value: str
45
- label: str
46
-
47
-
48
- class ConfigElement(CoreModel):
49
- selected: Optional[str] = None
50
- values: List[ConfigElementValue] = []
51
-
52
-
53
- class ConfigMultiElement(CoreModel):
54
- selected: List[str] = []
55
- values: List[ConfigElementValue] = []
@@ -1,6 +1,6 @@
1
1
  import re
2
2
  from enum import Enum
3
- from typing import Any, List, Optional, Union
3
+ from typing import Any, Dict, List, Optional, Union
4
4
 
5
5
  from pydantic import Field, ValidationError, conint, constr, root_validator, validator
6
6
  from typing_extensions import Annotated, Literal
@@ -11,8 +11,6 @@ from dstack._internal.core.models.envs import Env
11
11
  from dstack._internal.core.models.fleets import FleetConfiguration
12
12
  from dstack._internal.core.models.gateways import GatewayConfiguration
13
13
  from dstack._internal.core.models.profiles import ProfileParams, parse_off_duration
14
- from dstack._internal.core.models.repos.base import Repo
15
- from dstack._internal.core.models.repos.virtual import VirtualRepo
16
14
  from dstack._internal.core.models.resources import Range, ResourcesSpec
17
15
  from dstack._internal.core.models.services import AnyModel, OpenAIChatModel
18
16
  from dstack._internal.core.models.unix import UnixUser
@@ -93,7 +91,9 @@ class BaseRunConfiguration(CoreModel):
93
91
  Optional[str],
94
92
  Field(description="The run name. If not specified, a random name is generated"),
95
93
  ] = None
96
- image: Annotated[Optional[str], Field(description="The name of the Docker image to run")]
94
+ image: Annotated[Optional[str], Field(description="The name of the Docker image to run")] = (
95
+ None
96
+ )
97
97
  user: Annotated[
98
98
  Optional[str],
99
99
  Field(
@@ -104,7 +104,7 @@ class BaseRunConfiguration(CoreModel):
104
104
  ),
105
105
  ] = None
106
106
  privileged: Annotated[bool, Field(description="Run the container in privileged mode")] = False
107
- entrypoint: Annotated[Optional[str], Field(description="The Docker entrypoint")]
107
+ entrypoint: Annotated[Optional[str], Field(description="The Docker entrypoint")] = None
108
108
  working_dir: Annotated[
109
109
  Optional[str],
110
110
  Field(
@@ -119,17 +119,17 @@ class BaseRunConfiguration(CoreModel):
119
119
  home_dir: str = "/root"
120
120
  registry_auth: Annotated[
121
121
  Optional[RegistryAuth], Field(description="Credentials for pulling a private Docker image")
122
- ]
122
+ ] = None
123
123
  python: Annotated[
124
124
  Optional[PythonVersion],
125
125
  Field(description="The major version of Python. Mutually exclusive with `image`"),
126
- ]
126
+ ] = None
127
127
  nvcc: Annotated[
128
128
  Optional[bool],
129
129
  Field(
130
130
  description="Use image with NVIDIA CUDA Compiler (NVCC) included. Mutually exclusive with `image`"
131
131
  ),
132
- ]
132
+ ] = None
133
133
  single_branch: Annotated[
134
134
  Optional[bool],
135
135
  Field(
@@ -178,9 +178,6 @@ class BaseRunConfiguration(CoreModel):
178
178
  UnixUser.parse(v)
179
179
  return v
180
180
 
181
- def get_repo(self) -> Repo:
182
- return VirtualRepo()
183
-
184
181
 
185
182
  class BaseRunConfigurationWithPorts(BaseRunConfiguration):
186
183
  ports: Annotated[
@@ -208,8 +205,11 @@ class BaseRunConfigurationWithCommands(BaseRunConfiguration):
208
205
 
209
206
 
210
207
  class DevEnvironmentConfigurationParams(CoreModel):
211
- ide: Annotated[Literal["vscode"], Field(description="The IDE to run")]
212
- version: Annotated[Optional[str], Field(description="The version of the IDE")]
208
+ ide: Annotated[
209
+ Union[Literal["vscode"], Literal["cursor"]],
210
+ Field(description="The IDE to run. Supported values include `vscode` and `cursor`"),
211
+ ]
212
+ version: Annotated[Optional[str], Field(description="The version of the IDE")] = None
213
213
  init: Annotated[CommandsList, Field(description="The bash commands to run on startup")] = []
214
214
  inactivity_duration: Annotated[
215
215
  Optional[Union[Literal["off"], int, bool, str]],
@@ -221,10 +221,11 @@ class DevEnvironmentConfigurationParams(CoreModel):
221
221
  " Inactivity is defined as the absence of SSH connections to the"
222
222
  " dev environment, including VS Code connections, `ssh <run name>`"
223
223
  " shells, and attached `dstack apply` or `dstack attach` commands."
224
- " Use `off` for unlimited duration. Defaults to `off`"
224
+ " Use `off` for unlimited duration. Can be updated in-place."
225
+ " Defaults to `off`"
225
226
  )
226
227
  ),
227
- ]
228
+ ] = None
228
229
 
229
230
  @validator("inactivity_duration", pre=True, allow_reuse=True)
230
231
  def parse_inactivity_duration(
@@ -424,4 +425,9 @@ class DstackConfiguration(CoreModel):
424
425
  ]
425
426
 
426
427
  class Config:
427
- schema_extra = {"$schema": "http://json-schema.org/draft-07/schema#"}
428
+ @staticmethod
429
+ def schema_extra(schema: Dict[str, Any]):
430
+ schema["$schema"] = "http://json-schema.org/draft-07/schema#"
431
+ # Allow additionalProperties so that vscode and others not supporting
432
+ # top-level oneOf do not warn about properties being invalid.
433
+ schema["additionalProperties"] = True
@@ -102,10 +102,11 @@ class Env(BaseModel):
102
102
  def copy(self, **kwargs) -> Self:
103
103
  # Env.copy() is tricky because it copies only the hidden top-level {"__root__": {...}}
104
104
  # structure, not the actual nested dict representing the env itself.
105
- # To avoid possible bugs, we prohibit shallow copying altogether.
105
+ # So we copy __root__ explicitly in case of a shallow copy.
106
+ new_copy = super().copy(**kwargs)
106
107
  if not kwargs.get("deep", False):
107
- raise TypeError(f"shallow copying of {self.__class__.__name__} is prohibited")
108
- return super().copy(**kwargs)
108
+ new_copy.__root__ = new_copy.__root__.copy()
109
+ return new_copy
109
110
 
110
111
  def as_dict(self) -> Dict[str, str]:
111
112
  """
@@ -10,18 +10,17 @@ from typing_extensions import Annotated, Literal
10
10
  from dstack._internal.core.models.backends.base import BackendType
11
11
  from dstack._internal.core.models.common import CoreModel
12
12
  from dstack._internal.core.models.envs import Env
13
- from dstack._internal.core.models.instances import InstanceOfferWithAvailability, SSHKey
14
- from dstack._internal.core.models.pools import Instance
13
+ from dstack._internal.core.models.instances import Instance, InstanceOfferWithAvailability, SSHKey
15
14
  from dstack._internal.core.models.profiles import (
16
15
  Profile,
17
16
  ProfileParams,
18
17
  ProfileRetry,
19
18
  SpotPolicy,
20
19
  TerminationPolicy,
21
- parse_duration,
22
20
  parse_idle_duration,
23
21
  )
24
22
  from dstack._internal.core.models.resources import Range, ResourcesSpec
23
+ from dstack._internal.utils.json_schema import add_extra_schema_types
25
24
 
26
25
 
27
26
  class FleetStatus(str, Enum):
@@ -218,30 +217,27 @@ class InstanceGroupParams(CoreModel):
218
217
  Optional[float],
219
218
  Field(description="The maximum instance price per hour, in dollars", gt=0.0),
220
219
  ] = None
221
-
222
220
  idle_duration: Annotated[
223
221
  Optional[Union[Literal["off"], str, int]],
224
222
  Field(
225
223
  description="Time to wait before terminating idle instances. Defaults to `5m` for runs and `3d` for fleets. Use `off` for unlimited duration"
226
224
  ),
227
225
  ] = None
228
- # Deprecated:
229
- termination_policy: Annotated[
230
- Optional[TerminationPolicy],
231
- Field(
232
- description="Deprecated in favor of `idle_duration`",
233
- ),
234
- ] = None
235
- termination_idle_time: Annotated[
236
- Optional[Union[str, int]],
237
- Field(
238
- description="Deprecated in favor of `idle_duration`",
239
- ),
240
- ] = None
241
226
 
242
- _validate_termination_idle_time = validator(
243
- "termination_idle_time", pre=True, allow_reuse=True
244
- )(parse_duration)
227
+ # Deprecated and unused. Left for compatibility with 0.18 clients.
228
+ termination_policy: Annotated[Optional[TerminationPolicy], Field(exclude=True)] = None
229
+ termination_idle_time: Annotated[Optional[Union[str, int]], Field(exclude=True)] = None
230
+
231
+ class Config:
232
+ @staticmethod
233
+ def schema_extra(schema: Dict[str, Any], model: Type):
234
+ del schema["properties"]["termination_policy"]
235
+ del schema["properties"]["termination_idle_time"]
236
+ add_extra_schema_types(
237
+ schema["properties"]["nodes"],
238
+ extra_types=[{"type": "integer"}, {"type": "string"}],
239
+ )
240
+
245
241
  _validate_idle_duration = validator("idle_duration", pre=True, allow_reuse=True)(
246
242
  parse_idle_duration
247
243
  )
@@ -290,8 +286,7 @@ class FleetSpec(CoreModel):
290
286
 
291
287
 
292
288
  class Fleet(CoreModel):
293
- # id is Optional for backward compatibility within 0.18.x
294
- id: Optional[uuid.UUID] = None
289
+ id: uuid.UUID
295
290
  name: str
296
291
  project_name: str
297
292
  spec: FleetSpec
@@ -76,12 +76,12 @@ class Gateway(CoreModel):
76
76
  # The ip address of the gateway instance
77
77
  ip_address: Optional[str]
78
78
  instance_id: Optional[str]
79
+ wildcard_domain: Optional[str]
80
+ default: bool
79
81
  # TODO: configuration fields are duplicated on top-level for backward compatibility with 0.18.x
80
- # Remove in 0.19
82
+ # Remove after 0.19
81
83
  backend: BackendType
82
84
  region: str
83
- default: bool
84
- wildcard_domain: Optional[str]
85
85
 
86
86
 
87
87
  class GatewayPlan(CoreModel):
@@ -1,5 +1,7 @@
1
+ import datetime
1
2
  from enum import Enum
2
3
  from typing import List, Optional
4
+ from uuid import UUID
3
5
 
4
6
  import gpuhunt
5
7
  from pydantic import root_validator
@@ -167,3 +169,25 @@ class InstanceStatus(str, Enum):
167
169
  @classmethod
168
170
  def finished_statuses(cls) -> List["InstanceStatus"]:
169
171
  return [cls.TERMINATING, cls.TERMINATED]
172
+
173
+
174
+ class Instance(CoreModel):
175
+ id: UUID
176
+ project_name: str
177
+ backend: Optional[BackendType] = None
178
+ instance_type: Optional[InstanceType] = None
179
+ name: str
180
+ fleet_id: Optional[UUID] = None
181
+ fleet_name: Optional[str] = None
182
+ instance_num: int
183
+ job_name: Optional[str] = None # deprecated, always None (instance can have more than one job)
184
+ hostname: Optional[str] = None
185
+ status: InstanceStatus
186
+ unreachable: bool = False
187
+ termination_reason: Optional[str] = None
188
+ created: datetime.datetime
189
+ region: Optional[str] = None
190
+ availability_zone: Optional[str] = None
191
+ price: Optional[float] = None
192
+ total_blocks: Optional[int] = None
193
+ busy_blocks: int = 0