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
@@ -0,0 +1,82 @@
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.lambdalabs import api_client
9
+ from dstack._internal.core.backends.lambdalabs.backend import LambdaBackend
10
+ from dstack._internal.core.backends.lambdalabs.models import (
11
+ AnyLambdaBackendConfig,
12
+ LambdaBackendConfig,
13
+ LambdaBackendConfigWithCreds,
14
+ LambdaConfig,
15
+ LambdaCreds,
16
+ LambdaStoredConfig,
17
+ )
18
+ from dstack._internal.core.models.backends.base import (
19
+ BackendType,
20
+ )
21
+
22
+ REGIONS = [
23
+ "us-south-1",
24
+ "us-south-2",
25
+ "us-south-3",
26
+ "us-west-2",
27
+ "us-west-1",
28
+ "us-midwest-1",
29
+ "us-west-3",
30
+ "us-east-1",
31
+ "us-east-2",
32
+ "europe-central-1",
33
+ "asia-south-1",
34
+ "me-west-1",
35
+ "asia-northeast-1",
36
+ "asia-northeast-2",
37
+ ]
38
+
39
+ DEFAULT_REGION = "us-east-1"
40
+
41
+
42
+ class LambdaConfigurator(Configurator):
43
+ TYPE = BackendType.LAMBDA
44
+ BACKEND_CLASS = LambdaBackend
45
+
46
+ def validate_config(self, config: LambdaBackendConfigWithCreds, default_creds_enabled: bool):
47
+ self._validate_lambda_api_key(config.creds.api_key)
48
+
49
+ def create_backend(
50
+ self, project_name: str, config: LambdaBackendConfigWithCreds
51
+ ) -> BackendRecord:
52
+ if config.regions is None:
53
+ config.regions = REGIONS
54
+ return BackendRecord(
55
+ config=LambdaStoredConfig(
56
+ **LambdaBackendConfig.__response__.parse_obj(config).dict()
57
+ ).json(),
58
+ auth=LambdaCreds.parse_obj(config.creds).json(),
59
+ )
60
+
61
+ def get_backend_config(
62
+ self, record: BackendRecord, include_creds: bool
63
+ ) -> AnyLambdaBackendConfig:
64
+ config = self._get_config(record)
65
+ if include_creds:
66
+ return LambdaBackendConfigWithCreds.__response__.parse_obj(config)
67
+ return LambdaBackendConfig.__response__.parse_obj(config)
68
+
69
+ def get_backend(self, record: BackendRecord) -> LambdaBackend:
70
+ config = self._get_config(record)
71
+ return LambdaBackend(config=config)
72
+
73
+ def _get_config(self, record: BackendRecord) -> LambdaConfig:
74
+ return LambdaConfig.__response__(
75
+ **json.loads(record.config),
76
+ creds=LambdaCreds.parse_raw(record.auth),
77
+ )
78
+
79
+ def _validate_lambda_api_key(self, api_key: str):
80
+ client = api_client.LambdaAPIClient(api_key=api_key)
81
+ if not client.validate_api_key():
82
+ raise_invalid_credentials_error(fields=[["creds", "api_key"]])
@@ -0,0 +1,37 @@
1
+ from typing import Annotated, List, Literal, Optional, Union
2
+
3
+ from pydantic import Field
4
+
5
+ from dstack._internal.core.models.common import CoreModel
6
+
7
+
8
+ class LambdaAPIKeyCreds(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
+ AnyLambdaCreds = LambdaAPIKeyCreds
14
+ LambdaCreds = AnyLambdaCreds
15
+
16
+
17
+ class LambdaBackendConfig(CoreModel):
18
+ type: Annotated[Literal["lambda"], Field(description="The type of backend")] = "lambda"
19
+ regions: Annotated[
20
+ Optional[List[str]],
21
+ Field(description="The list of Lambda regions. Omit to use all regions"),
22
+ ] = None
23
+
24
+
25
+ class LambdaBackendConfigWithCreds(LambdaBackendConfig):
26
+ creds: Annotated[AnyLambdaCreds, Field(description="The credentials")]
27
+
28
+
29
+ AnyLambdaBackendConfig = Union[LambdaBackendConfig, LambdaBackendConfigWithCreds]
30
+
31
+
32
+ class LambdaStoredConfig(LambdaBackendConfig):
33
+ pass
34
+
35
+
36
+ class LambdaConfig(LambdaStoredConfig):
37
+ creds: AnyLambdaCreds
@@ -1,13 +0,0 @@
1
- from dstack._internal.core.backends.base import Backend
2
- from dstack._internal.core.backends.local.compute import LocalCompute
3
- from dstack._internal.core.models.backends.base import BackendType
4
-
5
-
6
- class LocalBackend(Backend):
7
- TYPE: BackendType = BackendType.LOCAL
8
-
9
- def __init__(self):
10
- self._compute = LocalCompute()
11
-
12
- def compute(self) -> LocalCompute:
13
- return self._compute
@@ -0,0 +1,14 @@
1
+ from dstack._internal.core.backends.base.backend import Backend
2
+ from dstack._internal.core.backends.local.compute import LocalCompute
3
+ from dstack._internal.core.models.backends.base import BackendType
4
+
5
+
6
+ class LocalBackend(Backend):
7
+ TYPE = BackendType.LOCAL
8
+ COMPUTE_CLASS = LocalCompute
9
+
10
+ def __init__(self):
11
+ self._compute = LocalCompute()
12
+
13
+ def compute(self) -> LocalCompute:
14
+ return self._compute
@@ -1,6 +1,10 @@
1
1
  from typing import List, Optional
2
2
 
3
- from dstack._internal.core.backends.base.compute import Compute
3
+ from dstack._internal.core.backends.base.compute import (
4
+ Compute,
5
+ ComputeWithCreateInstanceSupport,
6
+ ComputeWithVolumeSupport,
7
+ )
4
8
  from dstack._internal.core.consts import DSTACK_RUNNER_SSH_PORT
5
9
  from dstack._internal.core.models.backends.base import BackendType
6
10
  from dstack._internal.core.models.instances import (
@@ -18,7 +22,11 @@ from dstack._internal.utils.logging import get_logger
18
22
  logger = get_logger(__name__)
19
23
 
20
24
 
21
- class LocalCompute(Compute):
25
+ class LocalCompute(
26
+ ComputeWithCreateInstanceSupport,
27
+ ComputeWithVolumeSupport,
28
+ Compute,
29
+ ):
22
30
  def get_offers(
23
31
  self, requirements: Optional[Requirements] = None
24
32
  ) -> List[InstanceOfferWithAvailability]:
@@ -85,6 +93,12 @@ class LocalCompute(Compute):
85
93
  backend_data=None,
86
94
  )
87
95
 
96
+ def register_volume(self, volume: Volume) -> VolumeProvisioningData:
97
+ return VolumeProvisioningData(
98
+ volume_id=volume.volume_id,
99
+ size_gb=volume.configuration.size_gb,
100
+ )
101
+
88
102
  def create_volume(self, volume: Volume) -> VolumeProvisioningData:
89
103
  return VolumeProvisioningData(
90
104
  volume_id=volume.name,
@@ -0,0 +1,128 @@
1
+ from typing import Union
2
+
3
+ from dstack._internal.core.backends.aws.models import (
4
+ AWSBackendConfig,
5
+ AWSBackendConfigWithCreds,
6
+ )
7
+ from dstack._internal.core.backends.azure.models import (
8
+ AzureBackendConfig,
9
+ AzureBackendConfigWithCreds,
10
+ )
11
+ from dstack._internal.core.backends.cudo.models import (
12
+ CudoBackendConfig,
13
+ CudoBackendConfigWithCreds,
14
+ )
15
+ from dstack._internal.core.backends.datacrunch.models import (
16
+ DataCrunchBackendConfig,
17
+ DataCrunchBackendConfigWithCreds,
18
+ )
19
+ from dstack._internal.core.backends.dstack.models import (
20
+ DstackBackendConfig,
21
+ DstackBaseBackendConfig,
22
+ )
23
+ from dstack._internal.core.backends.gcp.models import (
24
+ GCPBackendConfig,
25
+ GCPBackendConfigWithCreds,
26
+ GCPBackendFileConfigWithCreds,
27
+ )
28
+ from dstack._internal.core.backends.kubernetes.models import (
29
+ KubernetesBackendConfig,
30
+ KubernetesBackendConfigWithCreds,
31
+ KubernetesBackendFileConfigWithCreds,
32
+ )
33
+ from dstack._internal.core.backends.lambdalabs.models import (
34
+ LambdaBackendConfig,
35
+ LambdaBackendConfigWithCreds,
36
+ )
37
+ from dstack._internal.core.backends.oci.models import (
38
+ OCIBackendConfig,
39
+ OCIBackendConfigWithCreds,
40
+ )
41
+ from dstack._internal.core.backends.runpod.models import (
42
+ RunpodBackendConfig,
43
+ RunpodBackendConfigWithCreds,
44
+ )
45
+ from dstack._internal.core.backends.tensordock.models import (
46
+ TensorDockBackendConfig,
47
+ TensorDockBackendConfigWithCreds,
48
+ )
49
+ from dstack._internal.core.backends.vastai.models import (
50
+ VastAIBackendConfig,
51
+ VastAIBackendConfigWithCreds,
52
+ )
53
+ from dstack._internal.core.backends.vultr.models import (
54
+ VultrBackendConfig,
55
+ VultrBackendConfigWithCreds,
56
+ )
57
+ from dstack._internal.core.models.common import CoreModel
58
+
59
+ # Backend config returned by the API
60
+ AnyBackendConfigWithoutCreds = Union[
61
+ AWSBackendConfig,
62
+ AzureBackendConfig,
63
+ CudoBackendConfig,
64
+ DataCrunchBackendConfig,
65
+ GCPBackendConfig,
66
+ KubernetesBackendConfig,
67
+ LambdaBackendConfig,
68
+ OCIBackendConfig,
69
+ RunpodBackendConfig,
70
+ TensorDockBackendConfig,
71
+ VastAIBackendConfig,
72
+ VultrBackendConfig,
73
+ DstackBackendConfig,
74
+ DstackBaseBackendConfig,
75
+ ]
76
+
77
+ # Same as AnyBackendConfigWithoutCreds but also includes creds.
78
+ # Used to create/update backend.
79
+ # Also returned by the API to project admins so that they can see/update backend creds.
80
+ AnyBackendConfigWithCreds = Union[
81
+ AWSBackendConfigWithCreds,
82
+ AzureBackendConfigWithCreds,
83
+ CudoBackendConfigWithCreds,
84
+ DataCrunchBackendConfigWithCreds,
85
+ GCPBackendConfigWithCreds,
86
+ KubernetesBackendConfigWithCreds,
87
+ LambdaBackendConfigWithCreds,
88
+ OCIBackendConfigWithCreds,
89
+ RunpodBackendConfigWithCreds,
90
+ TensorDockBackendConfigWithCreds,
91
+ VastAIBackendConfigWithCreds,
92
+ VultrBackendConfigWithCreds,
93
+ DstackBackendConfig,
94
+ ]
95
+
96
+ # Backend config accepted in server/config.yaml.
97
+ # This can be different from the API config.
98
+ # For example, it can make creds data optional and resolve it by filename.
99
+ AnyBackendFileConfigWithCreds = Union[
100
+ AWSBackendConfigWithCreds,
101
+ AzureBackendConfigWithCreds,
102
+ CudoBackendConfigWithCreds,
103
+ DataCrunchBackendConfigWithCreds,
104
+ GCPBackendFileConfigWithCreds,
105
+ KubernetesBackendFileConfigWithCreds,
106
+ LambdaBackendConfigWithCreds,
107
+ OCIBackendConfigWithCreds,
108
+ RunpodBackendConfigWithCreds,
109
+ TensorDockBackendConfigWithCreds,
110
+ VastAIBackendConfigWithCreds,
111
+ VultrBackendConfigWithCreds,
112
+ ]
113
+
114
+
115
+ # The API can return backend config with or without creds
116
+ AnyBackendConfig = Union[AnyBackendConfigWithoutCreds, AnyBackendConfigWithCreds]
117
+
118
+
119
+ # In case we'll support multiple backends of the same type,
120
+ # this adds backend name to backend config.
121
+ class BackendInfo(CoreModel):
122
+ name: str
123
+ config: AnyBackendConfigWithoutCreds
124
+
125
+
126
+ class BackendInfoYAML(CoreModel):
127
+ name: str
128
+ config_yaml: str
@@ -1,15 +0,0 @@
1
- from dstack._internal.core.backends.base import Backend
2
- from dstack._internal.core.backends.oci.compute import OCICompute
3
- from dstack._internal.core.backends.oci.config import OCIConfig
4
- from dstack._internal.core.models.backends.base import BackendType
5
-
6
-
7
- class OCIBackend(Backend):
8
- TYPE: BackendType = BackendType.OCI
9
-
10
- def __init__(self, config: OCIConfig):
11
- self.config = config
12
- self._compute = OCICompute(self.config)
13
-
14
- def compute(self) -> OCICompute:
15
- return self._compute
@@ -2,7 +2,7 @@ import oci
2
2
  from typing_extensions import Any, Mapping
3
3
 
4
4
  from dstack._internal.core.backends.oci.exceptions import any_oci_exception
5
- from dstack._internal.core.models.backends.oci import AnyOCICreds, OCIDefaultCreds
5
+ from dstack._internal.core.backends.oci.models import AnyOCICreds, OCIDefaultCreds
6
6
  from dstack._internal.core.models.common import is_core_model_instance
7
7
 
8
8
 
@@ -20,7 +20,3 @@ def creds_valid(creds: AnyOCICreds) -> bool:
20
20
  except any_oci_exception:
21
21
  return False
22
22
  return True
23
-
24
-
25
- def default_creds_available() -> bool:
26
- return creds_valid(OCIDefaultCreds())
@@ -0,0 +1,16 @@
1
+ from dstack._internal.core.backends.base.backend import Backend
2
+ from dstack._internal.core.backends.oci.compute import OCICompute
3
+ from dstack._internal.core.backends.oci.models import OCIConfig
4
+ from dstack._internal.core.models.backends.base import BackendType
5
+
6
+
7
+ class OCIBackend(Backend):
8
+ TYPE = BackendType.OCI
9
+ COMPUTE_CLASS = OCICompute
10
+
11
+ def __init__(self, config: OCIConfig):
12
+ self.config = config
13
+ self._compute = OCICompute(self.config)
14
+
15
+ def compute(self) -> OCICompute:
16
+ return self._compute
@@ -6,13 +6,14 @@ import oci
6
6
 
7
7
  from dstack._internal.core.backends.base.compute import (
8
8
  Compute,
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.oci import resources
15
- from dstack._internal.core.backends.oci.config import OCIConfig
16
+ from dstack._internal.core.backends.oci.models import OCIConfig
16
17
  from dstack._internal.core.backends.oci.region import make_region_clients_map
17
18
  from dstack._internal.core.errors import NoCapacityError
18
19
  from dstack._internal.core.models.backends.base import BackendType
@@ -21,11 +22,9 @@ from dstack._internal.core.models.instances import (
21
22
  InstanceConfiguration,
22
23
  InstanceOffer,
23
24
  InstanceOfferWithAvailability,
24
- SSHKey,
25
25
  )
26
26
  from dstack._internal.core.models.resources import Memory, Range
27
- from dstack._internal.core.models.runs import Job, JobProvisioningData, Requirements, Run
28
- from dstack._internal.core.models.volumes import Volume
27
+ from dstack._internal.core.models.runs import JobProvisioningData, Requirements
29
28
 
30
29
  SUPPORTED_SHAPE_FAMILIES = [
31
30
  "VM.Standard2.",
@@ -46,7 +45,11 @@ SUPPORTED_SHAPE_FAMILIES = [
46
45
  CONFIGURABLE_DISK_SIZE = Range[Memory](min=Memory.parse("50GB"), max=Memory.parse("32TB"))
47
46
 
48
47
 
49
- class OCICompute(Compute):
48
+ class OCICompute(
49
+ ComputeWithCreateInstanceSupport,
50
+ ComputeWithMultinodeSupport,
51
+ Compute,
52
+ ):
50
53
  def __init__(self, config: OCIConfig):
51
54
  super().__init__()
52
55
  self.config = config
@@ -92,23 +95,6 @@ class OCICompute(Compute):
92
95
 
93
96
  return offers_with_availability
94
97
 
95
- def run_job(
96
- self,
97
- run: Run,
98
- job: Job,
99
- instance_offer: InstanceOfferWithAvailability,
100
- project_ssh_public_key: str,
101
- project_ssh_private_key: str,
102
- volumes: List[Volume],
103
- ) -> JobProvisioningData:
104
- instance_config = InstanceConfiguration(
105
- project_name=run.project_name,
106
- instance_name=get_job_instance_name(run, job),
107
- ssh_keys=[SSHKey(public=project_ssh_public_key.strip())],
108
- user=run.user,
109
- )
110
- return self.create_instance(instance_offer, instance_config)
111
-
112
98
  def terminate_instance(
113
99
  self, instance_id: str, region: str, backend_data: Optional[str] = None
114
100
  ) -> None:
@@ -1,9 +1,23 @@
1
1
  import json
2
2
  from typing import Dict, Iterable, List, Set, Tuple
3
3
 
4
- from dstack._internal.core.backends.oci import OCIBackend, auth, resources
5
- from dstack._internal.core.backends.oci.config import OCIConfig
4
+ from dstack._internal.core.backends.base.configurator import (
5
+ BackendRecord,
6
+ Configurator,
7
+ raise_invalid_credentials_error,
8
+ )
9
+ from dstack._internal.core.backends.oci import resources
10
+ from dstack._internal.core.backends.oci.backend import OCIBackend
6
11
  from dstack._internal.core.backends.oci.exceptions import any_oci_exception
12
+ from dstack._internal.core.backends.oci.models import (
13
+ AnyOCIBackendConfig,
14
+ OCIBackendConfig,
15
+ OCIBackendConfigWithCreds,
16
+ OCIConfig,
17
+ OCICreds,
18
+ OCIDefaultCreds,
19
+ OCIStoredConfig,
20
+ )
7
21
  from dstack._internal.core.backends.oci.region import (
8
22
  get_subscribed_regions,
9
23
  make_region_client,
@@ -12,26 +26,8 @@ from dstack._internal.core.backends.oci.region import (
12
26
  from dstack._internal.core.errors import ServerClientError
13
27
  from dstack._internal.core.models.backends.base import (
14
28
  BackendType,
15
- ConfigElementValue,
16
- ConfigMultiElement,
17
- )
18
- from dstack._internal.core.models.backends.oci import (
19
- AnyOCIConfigInfo,
20
- OCIConfigInfo,
21
- OCIConfigInfoWithCreds,
22
- OCIConfigInfoWithCredsPartial,
23
- OCIConfigValues,
24
- OCICreds,
25
- OCIDefaultCreds,
26
- OCIStoredConfig,
27
29
  )
28
30
  from dstack._internal.core.models.common import is_core_model_instance
29
- from dstack._internal.server import settings
30
- from dstack._internal.server.models import BackendModel, DecryptedString, ProjectModel
31
- from dstack._internal.server.services.backends.configurators.base import (
32
- Configurator,
33
- raise_invalid_credentials_error,
34
- )
35
31
 
36
32
  # where dstack images are published
37
33
  SUPPORTED_REGIONS = frozenset(
@@ -48,56 +44,23 @@ SUPPORTED_REGIONS = frozenset(
48
44
 
49
45
 
50
46
  class OCIConfigurator(Configurator):
51
- TYPE: BackendType = BackendType.OCI
52
-
53
- def get_default_configs(self) -> List[OCIConfigInfoWithCreds]:
54
- creds = OCIDefaultCreds()
55
- try:
56
- subscribed_regions = get_subscribed_regions(creds).names
57
- except any_oci_exception:
58
- return []
59
- return [
60
- OCIConfigInfoWithCreds(
61
- regions=list(subscribed_regions & SUPPORTED_REGIONS),
62
- creds=creds,
63
- )
64
- ]
47
+ TYPE = BackendType.OCI
48
+ BACKEND_CLASS = OCIBackend
65
49
 
66
- def get_config_values(self, config: OCIConfigInfoWithCredsPartial) -> OCIConfigValues:
67
- config_values = OCIConfigValues(regions=None)
68
- config_values.default_creds = (
69
- settings.DEFAULT_CREDS_ENABLED and auth.default_creds_available()
70
- )
71
- if config.creds is None:
72
- return config_values
73
- if (
74
- is_core_model_instance(config.creds, OCIDefaultCreds)
75
- and not settings.DEFAULT_CREDS_ENABLED
76
- ):
50
+ def validate_config(self, config: OCIBackendConfigWithCreds, default_creds_enabled: bool):
51
+ if is_core_model_instance(config.creds, OCIDefaultCreds) and not default_creds_enabled:
77
52
  raise_invalid_credentials_error(
78
53
  fields=[["creds"]],
79
54
  details="Default credentials are forbidden by dstack settings",
80
55
  )
81
-
82
56
  try:
83
- available_regions = get_subscribed_regions(config.creds).names & SUPPORTED_REGIONS
57
+ get_subscribed_regions(config.creds).names
84
58
  except any_oci_exception as e:
85
59
  raise_invalid_credentials_error(fields=[["creds"]], details=e)
86
60
 
87
- if config.regions:
88
- selected_regions = [r for r in config.regions if r in available_regions]
89
- else:
90
- selected_regions = list(available_regions)
91
-
92
- config_values.regions = self._get_regions_element(
93
- available=available_regions,
94
- selected=selected_regions,
95
- )
96
- return config_values
97
-
98
61
  def create_backend(
99
- self, project: ProjectModel, config: OCIConfigInfoWithCreds
100
- ) -> BackendModel:
62
+ self, project_name: str, config: OCIBackendConfigWithCreds
63
+ ) -> BackendRecord:
101
64
  try:
102
65
  subscribed_regions = get_subscribed_regions(config.creds)
103
66
  except any_oci_exception as e:
@@ -109,44 +72,36 @@ class OCIConfigurator(Configurator):
109
72
  _raise_if_regions_unavailable(config.regions, subscribed_regions.names)
110
73
 
111
74
  compartment_id, subnet_ids_per_region = _create_resources(
112
- project, config, subscribed_regions.home_region_name
75
+ project_name, config, subscribed_regions.home_region_name
113
76
  )
114
77
  config.compartment_id = compartment_id
115
78
  stored_config = OCIStoredConfig.__response__(
116
79
  **config.dict(), subnet_ids_per_region=subnet_ids_per_region
117
80
  )
118
81
 
119
- return BackendModel(
120
- project_id=project.id,
121
- type=self.TYPE.value,
82
+ return BackendRecord(
122
83
  config=stored_config.json(),
123
- auth=DecryptedString(plaintext=OCICreds.parse_obj(config.creds).json()),
84
+ auth=OCICreds.parse_obj(config.creds).json(),
124
85
  )
125
86
 
126
- def get_config_info(self, model: BackendModel, include_creds: bool) -> AnyOCIConfigInfo:
127
- config = self._get_backend_config(model)
87
+ def get_backend_config(
88
+ self, record: BackendRecord, include_creds: bool
89
+ ) -> AnyOCIBackendConfig:
90
+ config = self._get_config(record)
128
91
  if include_creds:
129
- return OCIConfigInfoWithCreds.__response__.parse_obj(config)
130
- return OCIConfigInfo.__response__.parse_obj(config)
92
+ return OCIBackendConfigWithCreds.__response__.parse_obj(config)
93
+ return OCIBackendConfig.__response__.parse_obj(config)
131
94
 
132
- def get_backend(self, model: BackendModel) -> OCIBackend:
133
- config = self._get_backend_config(model)
95
+ def get_backend(self, record: BackendRecord) -> OCIBackend:
96
+ config = self._get_config(record)
134
97
  return OCIBackend(config=config)
135
98
 
136
- def _get_backend_config(self, model: BackendModel) -> OCIConfig:
99
+ def _get_config(self, record: BackendRecord) -> OCIConfig:
137
100
  return OCIConfig.__response__(
138
- **json.loads(model.config),
139
- creds=OCICreds.parse_raw(model.auth.get_plaintext_or_error()).__root__,
101
+ **json.loads(record.config),
102
+ creds=OCICreds.parse_raw(record.auth).__root__,
140
103
  )
141
104
 
142
- def _get_regions_element(
143
- self, available: Iterable[str], selected: List[str]
144
- ) -> ConfigMultiElement:
145
- element = ConfigMultiElement(selected=selected)
146
- for region in available:
147
- element.values.append(ConfigElementValue(value=region, label=region))
148
- return element
149
-
150
105
 
151
106
  def _filter_supported_regions(subscribed_region_names: Set[str]) -> List[str]:
152
107
  available_regions = subscribed_region_names & SUPPORTED_REGIONS
@@ -178,13 +133,13 @@ def _raise_if_regions_unavailable(
178
133
 
179
134
 
180
135
  def _create_resources(
181
- project: ProjectModel, config: OCIConfigInfoWithCreds, home_region: str
136
+ project_name: str, config: OCIBackendConfigWithCreds, home_region: str
182
137
  ) -> Tuple[str, Dict[str, str]]:
183
138
  compartment_id = config.compartment_id
184
139
  if not compartment_id:
185
140
  home_region_client = make_region_client(home_region, config.creds)
186
141
  compartment_id = resources.get_or_create_compartment(
187
- f"dstack-{project.name}",
142
+ f"dstack-{project_name}",
188
143
  home_region_client.client_config["tenancy"],
189
144
  home_region_client.identity_client,
190
145
  ).id
@@ -192,7 +147,7 @@ def _create_resources(
192
147
  region_clients = make_region_clients_map(config.regions, config.creds)
193
148
  resources.wait_until_compartment_active(compartment_id, region_clients)
194
149
  subnets_per_region = resources.set_up_network_resources(
195
- compartment_id, project.name, region_clients
150
+ compartment_id, project_name, region_clients
196
151
  )
197
152
 
198
153
  return compartment_id, subnets_per_region