dstack 0.18.43__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.
- dstack/_internal/cli/commands/gateway.py +15 -3
- dstack/_internal/cli/commands/logs.py +0 -22
- dstack/_internal/cli/commands/stats.py +8 -17
- dstack/_internal/cli/main.py +1 -5
- dstack/_internal/cli/services/configurators/fleet.py +4 -39
- dstack/_internal/cli/services/configurators/run.py +22 -20
- dstack/_internal/cli/services/profile.py +34 -83
- dstack/_internal/cli/utils/gateway.py +1 -1
- dstack/_internal/cli/utils/run.py +11 -0
- dstack/_internal/core/backends/__init__.py +56 -39
- dstack/_internal/core/backends/aws/__init__.py +0 -25
- dstack/_internal/core/backends/aws/auth.py +1 -10
- dstack/_internal/core/backends/aws/backend.py +26 -0
- dstack/_internal/core/backends/aws/compute.py +21 -45
- dstack/_internal/{server/services/backends/configurators/aws.py → core/backends/aws/configurator.py} +46 -85
- dstack/_internal/core/backends/aws/models.py +135 -0
- dstack/_internal/core/backends/aws/resources.py +1 -1
- dstack/_internal/core/backends/azure/__init__.py +0 -20
- dstack/_internal/core/backends/azure/auth.py +2 -11
- dstack/_internal/core/backends/azure/backend.py +21 -0
- dstack/_internal/core/backends/azure/compute.py +14 -28
- dstack/_internal/{server/services/backends/configurators/azure.py → core/backends/azure/configurator.py} +141 -210
- dstack/_internal/core/backends/azure/models.py +89 -0
- dstack/_internal/core/backends/base/__init__.py +0 -12
- dstack/_internal/core/backends/base/backend.py +18 -0
- dstack/_internal/core/backends/base/compute.py +153 -33
- dstack/_internal/core/backends/base/configurator.py +105 -0
- dstack/_internal/core/backends/base/models.py +14 -0
- dstack/_internal/core/backends/configurators.py +138 -0
- dstack/_internal/core/backends/cudo/__init__.py +0 -15
- dstack/_internal/core/backends/cudo/backend.py +16 -0
- dstack/_internal/core/backends/cudo/compute.py +8 -26
- dstack/_internal/core/backends/cudo/configurator.py +72 -0
- dstack/_internal/core/backends/cudo/models.py +37 -0
- dstack/_internal/core/backends/datacrunch/__init__.py +0 -15
- dstack/_internal/core/backends/datacrunch/backend.py +16 -0
- dstack/_internal/core/backends/datacrunch/compute.py +8 -25
- dstack/_internal/core/backends/datacrunch/configurator.py +66 -0
- dstack/_internal/core/backends/datacrunch/models.py +38 -0
- dstack/_internal/core/{models/backends/dstack.py → backends/dstack/models.py} +7 -7
- dstack/_internal/core/backends/gcp/__init__.py +0 -16
- dstack/_internal/core/backends/gcp/auth.py +2 -11
- dstack/_internal/core/backends/gcp/backend.py +17 -0
- dstack/_internal/core/backends/gcp/compute.py +14 -44
- dstack/_internal/{server/services/backends/configurators/gcp.py → core/backends/gcp/configurator.py} +46 -103
- dstack/_internal/core/backends/gcp/models.py +125 -0
- dstack/_internal/core/backends/kubernetes/__init__.py +0 -15
- dstack/_internal/core/backends/kubernetes/backend.py +16 -0
- dstack/_internal/core/backends/kubernetes/compute.py +16 -5
- dstack/_internal/core/backends/kubernetes/configurator.py +55 -0
- dstack/_internal/core/backends/kubernetes/models.py +72 -0
- dstack/_internal/core/backends/lambdalabs/__init__.py +0 -16
- dstack/_internal/core/backends/lambdalabs/backend.py +17 -0
- dstack/_internal/core/backends/lambdalabs/compute.py +7 -28
- dstack/_internal/core/backends/lambdalabs/configurator.py +82 -0
- dstack/_internal/core/backends/lambdalabs/models.py +37 -0
- dstack/_internal/core/backends/local/__init__.py +0 -13
- dstack/_internal/core/backends/local/backend.py +14 -0
- dstack/_internal/core/backends/local/compute.py +16 -2
- dstack/_internal/core/backends/models.py +128 -0
- dstack/_internal/core/backends/oci/__init__.py +0 -15
- dstack/_internal/core/backends/oci/auth.py +1 -5
- dstack/_internal/core/backends/oci/backend.py +16 -0
- dstack/_internal/core/backends/oci/compute.py +9 -23
- dstack/_internal/{server/services/backends/configurators/oci.py → core/backends/oci/configurator.py} +40 -85
- dstack/_internal/core/{models/backends/oci.py → backends/oci/models.py} +24 -25
- dstack/_internal/core/backends/oci/region.py +1 -1
- dstack/_internal/core/backends/runpod/__init__.py +0 -15
- dstack/_internal/core/backends/runpod/backend.py +16 -0
- dstack/_internal/core/backends/runpod/compute.py +28 -6
- dstack/_internal/core/backends/runpod/configurator.py +59 -0
- dstack/_internal/core/backends/runpod/models.py +54 -0
- dstack/_internal/core/backends/template/__init__.py +0 -0
- dstack/_internal/core/backends/tensordock/__init__.py +0 -15
- dstack/_internal/core/backends/tensordock/backend.py +16 -0
- dstack/_internal/core/backends/tensordock/compute.py +8 -27
- dstack/_internal/core/backends/tensordock/configurator.py +68 -0
- dstack/_internal/core/backends/tensordock/models.py +38 -0
- dstack/_internal/core/backends/vastai/__init__.py +0 -15
- dstack/_internal/core/backends/vastai/backend.py +16 -0
- dstack/_internal/core/backends/vastai/compute.py +2 -2
- dstack/_internal/core/backends/vastai/configurator.py +66 -0
- dstack/_internal/core/backends/vastai/models.py +37 -0
- dstack/_internal/core/backends/vultr/__init__.py +0 -15
- dstack/_internal/core/backends/vultr/backend.py +16 -0
- dstack/_internal/core/backends/vultr/compute.py +10 -24
- dstack/_internal/core/backends/vultr/configurator.py +64 -0
- dstack/_internal/core/backends/vultr/models.py +34 -0
- dstack/_internal/core/models/backends/__init__.py +0 -184
- dstack/_internal/core/models/backends/base.py +0 -19
- dstack/_internal/core/models/configurations.py +22 -16
- dstack/_internal/core/models/envs.py +4 -3
- dstack/_internal/core/models/fleets.py +17 -22
- dstack/_internal/core/models/gateways.py +3 -3
- dstack/_internal/core/models/instances.py +24 -0
- dstack/_internal/core/models/profiles.py +85 -45
- dstack/_internal/core/models/projects.py +1 -1
- dstack/_internal/core/models/repos/base.py +0 -5
- dstack/_internal/core/models/repos/local.py +3 -3
- dstack/_internal/core/models/repos/remote.py +26 -12
- dstack/_internal/core/models/repos/virtual.py +1 -1
- dstack/_internal/core/models/resources.py +45 -76
- dstack/_internal/core/models/runs.py +21 -19
- dstack/_internal/core/models/volumes.py +1 -3
- dstack/_internal/core/services/profiles.py +7 -16
- dstack/_internal/core/services/repos.py +0 -4
- dstack/_internal/server/app.py +11 -4
- dstack/_internal/server/background/__init__.py +10 -0
- dstack/_internal/server/background/tasks/process_gateways.py +4 -8
- dstack/_internal/server/background/tasks/process_instances.py +14 -9
- dstack/_internal/server/background/tasks/process_metrics.py +1 -1
- dstack/_internal/server/background/tasks/process_placement_groups.py +5 -1
- dstack/_internal/server/background/tasks/process_prometheus_metrics.py +135 -0
- dstack/_internal/server/background/tasks/process_running_jobs.py +80 -24
- dstack/_internal/server/background/tasks/process_runs.py +1 -0
- dstack/_internal/server/background/tasks/process_submitted_jobs.py +20 -38
- dstack/_internal/server/background/tasks/process_volumes.py +5 -2
- dstack/_internal/server/migrations/versions/60e444118b6d_add_jobprometheusmetrics.py +40 -0
- dstack/_internal/server/migrations/versions/7bc2586e8b9e_make_instancemodel_pool_id_optional.py +36 -0
- dstack/_internal/server/migrations/versions/98d1b92988bc_add_jobterminationreason_terminated_due_.py +140 -0
- dstack/_internal/server/migrations/versions/bc8ca4a505c6_store_backendtype_as_string.py +171 -0
- dstack/_internal/server/models.py +59 -9
- dstack/_internal/server/routers/backends.py +14 -23
- dstack/_internal/server/routers/instances.py +3 -4
- dstack/_internal/server/routers/metrics.py +31 -10
- dstack/_internal/server/routers/prometheus.py +36 -0
- dstack/_internal/server/routers/repos.py +1 -2
- dstack/_internal/server/routers/runs.py +13 -59
- dstack/_internal/server/schemas/gateways.py +14 -23
- dstack/_internal/server/schemas/projects.py +7 -2
- dstack/_internal/server/schemas/repos.py +2 -38
- dstack/_internal/server/schemas/runner.py +1 -0
- dstack/_internal/server/schemas/runs.py +1 -24
- dstack/_internal/server/security/permissions.py +1 -1
- dstack/_internal/server/services/backends/__init__.py +85 -158
- dstack/_internal/server/services/config.py +53 -567
- dstack/_internal/server/services/fleets.py +9 -103
- dstack/_internal/server/services/gateways/__init__.py +13 -4
- dstack/_internal/server/services/{pools.py → instances.py} +22 -329
- dstack/_internal/server/services/jobs/__init__.py +9 -6
- dstack/_internal/server/services/jobs/configurators/base.py +25 -1
- dstack/_internal/server/services/jobs/configurators/dev.py +9 -1
- dstack/_internal/server/services/jobs/configurators/extensions/cursor.py +42 -0
- dstack/_internal/server/services/metrics.py +131 -72
- dstack/_internal/server/services/offers.py +1 -1
- dstack/_internal/server/services/projects.py +23 -14
- dstack/_internal/server/services/prometheus.py +245 -0
- dstack/_internal/server/services/runner/client.py +14 -3
- dstack/_internal/server/services/runs.py +67 -31
- dstack/_internal/server/services/volumes.py +9 -4
- dstack/_internal/server/settings.py +3 -0
- dstack/_internal/server/statics/index.html +1 -1
- dstack/_internal/server/statics/{main-fe8fd9db55df8d10e648.js → main-4a0fe83e84574654e397.js} +76 -19
- dstack/_internal/server/statics/{main-fe8fd9db55df8d10e648.js.map → main-4a0fe83e84574654e397.js.map} +1 -1
- dstack/_internal/server/statics/{main-7510e71dfa9749a4e70e.css → main-da9f8c06a69c20dac23e.css} +1 -1
- dstack/_internal/server/statics/static/media/entraID.d65d1f3e9486a8e56d24fc07b3230885.svg +9 -0
- dstack/_internal/server/testing/common.py +75 -32
- dstack/_internal/utils/json_schema.py +6 -0
- dstack/_internal/utils/ssh.py +2 -1
- dstack/api/__init__.py +4 -0
- dstack/api/_public/__init__.py +16 -20
- dstack/api/_public/backends.py +1 -1
- dstack/api/_public/repos.py +36 -36
- dstack/api/_public/runs.py +170 -83
- dstack/api/server/__init__.py +11 -13
- dstack/api/server/_backends.py +12 -16
- dstack/api/server/_fleets.py +15 -55
- dstack/api/server/_gateways.py +3 -14
- dstack/api/server/_repos.py +1 -4
- dstack/api/server/_runs.py +21 -96
- dstack/api/server/_volumes.py +10 -5
- dstack/api/utils.py +3 -0
- dstack/version.py +1 -1
- {dstack-0.18.43.dist-info → dstack-0.19.0.dist-info}/METADATA +10 -1
- {dstack-0.18.43.dist-info → dstack-0.19.0.dist-info}/RECORD +229 -206
- tests/_internal/cli/services/configurators/test_profile.py +6 -6
- tests/_internal/core/backends/aws/test_configurator.py +35 -0
- tests/_internal/core/backends/aws/test_resources.py +1 -1
- tests/_internal/core/backends/azure/test_configurator.py +61 -0
- tests/_internal/core/backends/cudo/__init__.py +0 -0
- tests/_internal/core/backends/cudo/test_configurator.py +37 -0
- tests/_internal/core/backends/datacrunch/__init__.py +0 -0
- tests/_internal/core/backends/datacrunch/test_configurator.py +17 -0
- tests/_internal/core/backends/gcp/test_configurator.py +42 -0
- tests/_internal/core/backends/kubernetes/test_configurator.py +43 -0
- tests/_internal/core/backends/lambdalabs/__init__.py +0 -0
- tests/_internal/core/backends/lambdalabs/test_configurator.py +38 -0
- tests/_internal/core/backends/oci/test_configurator.py +55 -0
- tests/_internal/core/backends/runpod/__init__.py +0 -0
- tests/_internal/core/backends/runpod/test_configurator.py +33 -0
- tests/_internal/core/backends/tensordock/__init__.py +0 -0
- tests/_internal/core/backends/tensordock/test_configurator.py +38 -0
- tests/_internal/core/backends/vastai/__init__.py +0 -0
- tests/_internal/core/backends/vastai/test_configurator.py +33 -0
- tests/_internal/core/backends/vultr/__init__.py +0 -0
- tests/_internal/core/backends/vultr/test_configurator.py +33 -0
- tests/_internal/server/background/tasks/test_process_gateways.py +4 -0
- tests/_internal/server/background/tasks/test_process_instances.py +49 -48
- tests/_internal/server/background/tasks/test_process_metrics.py +0 -3
- tests/_internal/server/background/tasks/test_process_placement_groups.py +2 -0
- tests/_internal/server/background/tasks/test_process_prometheus_metrics.py +186 -0
- tests/_internal/server/background/tasks/test_process_running_jobs.py +123 -19
- tests/_internal/server/background/tasks/test_process_runs.py +8 -22
- tests/_internal/server/background/tasks/test_process_submitted_jobs.py +3 -40
- tests/_internal/server/background/tasks/test_process_submitted_volumes.py +2 -0
- tests/_internal/server/background/tasks/test_process_terminating_jobs.py +10 -15
- tests/_internal/server/routers/test_backends.py +6 -764
- tests/_internal/server/routers/test_fleets.py +2 -26
- tests/_internal/server/routers/test_gateways.py +27 -3
- tests/_internal/server/routers/test_instances.py +0 -10
- tests/_internal/server/routers/test_metrics.py +42 -0
- tests/_internal/server/routers/test_projects.py +56 -0
- tests/_internal/server/routers/test_prometheus.py +333 -0
- tests/_internal/server/routers/test_repos.py +0 -15
- tests/_internal/server/routers/test_runs.py +83 -275
- tests/_internal/server/routers/test_volumes.py +2 -3
- tests/_internal/server/services/backends/__init__.py +0 -0
- tests/_internal/server/services/jobs/configurators/test_task.py +35 -0
- tests/_internal/server/services/test_config.py +7 -4
- tests/_internal/server/services/test_fleets.py +1 -4
- tests/_internal/server/services/{test_pools.py → test_instances.py} +11 -49
- tests/_internal/server/services/test_metrics.py +167 -0
- tests/_internal/server/services/test_repos.py +1 -14
- tests/_internal/server/services/test_runs.py +0 -4
- dstack/_internal/cli/commands/pool.py +0 -581
- dstack/_internal/cli/commands/run.py +0 -75
- dstack/_internal/core/backends/aws/config.py +0 -18
- dstack/_internal/core/backends/azure/config.py +0 -12
- dstack/_internal/core/backends/base/config.py +0 -5
- dstack/_internal/core/backends/cudo/config.py +0 -9
- dstack/_internal/core/backends/datacrunch/config.py +0 -9
- dstack/_internal/core/backends/gcp/config.py +0 -22
- dstack/_internal/core/backends/kubernetes/config.py +0 -6
- dstack/_internal/core/backends/lambdalabs/config.py +0 -9
- dstack/_internal/core/backends/nebius/__init__.py +0 -15
- dstack/_internal/core/backends/nebius/api_client.py +0 -319
- dstack/_internal/core/backends/nebius/compute.py +0 -220
- dstack/_internal/core/backends/nebius/config.py +0 -6
- dstack/_internal/core/backends/nebius/types.py +0 -37
- dstack/_internal/core/backends/oci/config.py +0 -6
- dstack/_internal/core/backends/runpod/config.py +0 -9
- dstack/_internal/core/backends/tensordock/config.py +0 -9
- dstack/_internal/core/backends/vastai/config.py +0 -6
- dstack/_internal/core/backends/vultr/config.py +0 -9
- dstack/_internal/core/models/backends/aws.py +0 -86
- dstack/_internal/core/models/backends/azure.py +0 -68
- dstack/_internal/core/models/backends/cudo.py +0 -43
- dstack/_internal/core/models/backends/datacrunch.py +0 -44
- dstack/_internal/core/models/backends/gcp.py +0 -67
- dstack/_internal/core/models/backends/kubernetes.py +0 -40
- dstack/_internal/core/models/backends/lambdalabs.py +0 -43
- dstack/_internal/core/models/backends/nebius.py +0 -54
- dstack/_internal/core/models/backends/runpod.py +0 -40
- dstack/_internal/core/models/backends/tensordock.py +0 -44
- dstack/_internal/core/models/backends/vastai.py +0 -43
- dstack/_internal/core/models/backends/vultr.py +0 -40
- dstack/_internal/core/models/pools.py +0 -43
- dstack/_internal/server/routers/pools.py +0 -142
- dstack/_internal/server/schemas/pools.py +0 -38
- dstack/_internal/server/services/backends/configurators/base.py +0 -72
- dstack/_internal/server/services/backends/configurators/cudo.py +0 -87
- dstack/_internal/server/services/backends/configurators/datacrunch.py +0 -79
- dstack/_internal/server/services/backends/configurators/kubernetes.py +0 -63
- dstack/_internal/server/services/backends/configurators/lambdalabs.py +0 -98
- dstack/_internal/server/services/backends/configurators/nebius.py +0 -85
- dstack/_internal/server/services/backends/configurators/runpod.py +0 -97
- dstack/_internal/server/services/backends/configurators/tensordock.py +0 -82
- dstack/_internal/server/services/backends/configurators/vastai.py +0 -80
- dstack/_internal/server/services/backends/configurators/vultr.py +0 -80
- dstack/api/_public/pools.py +0 -41
- dstack/api/_public/resources.py +0 -105
- dstack/api/server/_pools.py +0 -63
- tests/_internal/server/routers/test_pools.py +0 -612
- /dstack/_internal/{server/services/backends/configurators → core/backends/dstack}/__init__.py +0 -0
- {dstack-0.18.43.dist-info → dstack-0.19.0.dist-info}/LICENSE.md +0 -0
- {dstack-0.18.43.dist-info → dstack-0.19.0.dist-info}/WHEEL +0 -0
- {dstack-0.18.43.dist-info → dstack-0.19.0.dist-info}/entry_points.txt +0 -0
- {dstack-0.18.43.dist-info → dstack-0.19.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,72 @@
|
|
|
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.cudo import api_client
|
|
9
|
+
from dstack._internal.core.backends.cudo.backend import CudoBackend
|
|
10
|
+
from dstack._internal.core.backends.cudo.models import (
|
|
11
|
+
AnyCudoBackendConfig,
|
|
12
|
+
CudoBackendConfig,
|
|
13
|
+
CudoBackendConfigWithCreds,
|
|
14
|
+
CudoConfig,
|
|
15
|
+
CudoCreds,
|
|
16
|
+
CudoStoredConfig,
|
|
17
|
+
)
|
|
18
|
+
from dstack._internal.core.models.backends.base import BackendType
|
|
19
|
+
|
|
20
|
+
REGIONS = [
|
|
21
|
+
"no-luster-1",
|
|
22
|
+
"se-smedjebacken-1",
|
|
23
|
+
"gb-london-1",
|
|
24
|
+
"se-stockholm-1",
|
|
25
|
+
"us-newyork-1",
|
|
26
|
+
"us-santaclara-1",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
DEFAULT_REGION = "no-luster-1"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class CudoConfigurator(Configurator):
|
|
33
|
+
TYPE = BackendType.CUDO
|
|
34
|
+
BACKEND_CLASS = CudoBackend
|
|
35
|
+
|
|
36
|
+
def validate_config(self, config: CudoBackendConfigWithCreds, default_creds_enabled: bool):
|
|
37
|
+
self._validate_cudo_api_key(config.creds.api_key)
|
|
38
|
+
|
|
39
|
+
def create_backend(
|
|
40
|
+
self, project_name: str, config: CudoBackendConfigWithCreds
|
|
41
|
+
) -> BackendRecord:
|
|
42
|
+
if config.regions is None:
|
|
43
|
+
config.regions = REGIONS
|
|
44
|
+
return BackendRecord(
|
|
45
|
+
config=CudoStoredConfig(
|
|
46
|
+
**CudoBackendConfig.__response__.parse_obj(config).dict()
|
|
47
|
+
).json(),
|
|
48
|
+
auth=CudoCreds.parse_obj(config.creds).json(),
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
def get_backend_config(
|
|
52
|
+
self, record: BackendRecord, include_creds: bool
|
|
53
|
+
) -> AnyCudoBackendConfig:
|
|
54
|
+
config = self._get_config(record)
|
|
55
|
+
if include_creds:
|
|
56
|
+
return CudoBackendConfigWithCreds.__response__.parse_obj(config)
|
|
57
|
+
return CudoBackendConfig.__response__.parse_obj(config)
|
|
58
|
+
|
|
59
|
+
def get_backend(self, record: BackendRecord) -> CudoBackend:
|
|
60
|
+
config = self._get_config(record)
|
|
61
|
+
return CudoBackend(config=config)
|
|
62
|
+
|
|
63
|
+
def _get_config(self, record: BackendRecord) -> CudoConfig:
|
|
64
|
+
return CudoConfig.__response__(
|
|
65
|
+
**json.loads(record.config),
|
|
66
|
+
creds=CudoCreds.parse_raw(record.auth),
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
def _validate_cudo_api_key(self, api_key: str):
|
|
70
|
+
client = api_client.CudoApiClient(api_key=api_key)
|
|
71
|
+
if not client.validate_api_key():
|
|
72
|
+
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 CudoAPIKeyCreds(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
|
+
AnyCudoCreds = CudoAPIKeyCreds
|
|
14
|
+
CudoCreds = AnyCudoCreds
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class CudoBackendConfig(CoreModel):
|
|
18
|
+
type: Annotated[Literal["cudo"], Field(description="The type of backend")] = "cudo"
|
|
19
|
+
regions: Annotated[
|
|
20
|
+
Optional[List[str]], Field(description="The list of Cudo regions. Omit to use all regions")
|
|
21
|
+
] = None
|
|
22
|
+
project_id: Annotated[str, Field(description="The project ID")]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class CudoBackendConfigWithCreds(CudoBackendConfig):
|
|
26
|
+
creds: Annotated[AnyCudoCreds, Field(description="The credentials")]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
AnyCudoBackendConfig = Union[CudoBackendConfig, CudoBackendConfigWithCreds]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class CudoStoredConfig(CudoBackendConfig):
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class CudoConfig(CudoStoredConfig):
|
|
37
|
+
creds: AnyCudoCreds
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
from dstack._internal.core.backends.base import Backend
|
|
2
|
-
from dstack._internal.core.backends.datacrunch.compute import DataCrunchCompute
|
|
3
|
-
from dstack._internal.core.backends.datacrunch.config import DataCrunchConfig
|
|
4
|
-
from dstack._internal.core.models.backends.base import BackendType
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
class DataCrunchBackend(Backend):
|
|
8
|
-
TYPE: BackendType = BackendType.DATACRUNCH
|
|
9
|
-
|
|
10
|
-
def __init__(self, config: DataCrunchConfig):
|
|
11
|
-
self.config = config
|
|
12
|
-
self._compute = DataCrunchCompute(self.config)
|
|
13
|
-
|
|
14
|
-
def compute(self) -> DataCrunchCompute:
|
|
15
|
-
return self._compute
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from dstack._internal.core.backends.base.backend import Backend
|
|
2
|
+
from dstack._internal.core.backends.datacrunch.compute import DataCrunchCompute
|
|
3
|
+
from dstack._internal.core.backends.datacrunch.models import DataCrunchConfig
|
|
4
|
+
from dstack._internal.core.models.backends.base import BackendType
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class DataCrunchBackend(Backend):
|
|
8
|
+
TYPE = BackendType.DATACRUNCH
|
|
9
|
+
COMPUTE_CLASS = DataCrunchCompute
|
|
10
|
+
|
|
11
|
+
def __init__(self, config: DataCrunchConfig):
|
|
12
|
+
self.config = config
|
|
13
|
+
self._compute = DataCrunchCompute(self.config)
|
|
14
|
+
|
|
15
|
+
def compute(self) -> DataCrunchCompute:
|
|
16
|
+
return self._compute
|
|
@@ -1,24 +1,23 @@
|
|
|
1
1
|
from typing import Dict, List, Optional
|
|
2
2
|
|
|
3
|
-
from dstack._internal.core.backends.base import Compute
|
|
3
|
+
from dstack._internal.core.backends.base.backend import Compute
|
|
4
4
|
from dstack._internal.core.backends.base.compute import (
|
|
5
|
+
ComputeWithCreateInstanceSupport,
|
|
5
6
|
generate_unique_instance_name,
|
|
6
7
|
get_shim_commands,
|
|
7
8
|
)
|
|
8
9
|
from dstack._internal.core.backends.base.offers import get_catalog_offers
|
|
9
10
|
from dstack._internal.core.backends.datacrunch.api_client import DataCrunchAPIClient
|
|
10
|
-
from dstack._internal.core.backends.datacrunch.
|
|
11
|
+
from dstack._internal.core.backends.datacrunch.models import DataCrunchConfig
|
|
11
12
|
from dstack._internal.core.models.backends.base import BackendType
|
|
12
13
|
from dstack._internal.core.models.instances import (
|
|
13
14
|
InstanceAvailability,
|
|
14
15
|
InstanceConfiguration,
|
|
15
16
|
InstanceOffer,
|
|
16
17
|
InstanceOfferWithAvailability,
|
|
17
|
-
SSHKey,
|
|
18
18
|
)
|
|
19
19
|
from dstack._internal.core.models.resources import Memory, Range
|
|
20
|
-
from dstack._internal.core.models.runs import
|
|
21
|
-
from dstack._internal.core.models.volumes import Volume
|
|
20
|
+
from dstack._internal.core.models.runs import JobProvisioningData, Requirements
|
|
22
21
|
from dstack._internal.utils.logging import get_logger
|
|
23
22
|
|
|
24
23
|
logger = get_logger("datacrunch.compute")
|
|
@@ -33,7 +32,10 @@ IMAGE_SIZE = Memory.parse("50GB")
|
|
|
33
32
|
CONFIGURABLE_DISK_SIZE = Range[Memory](min=IMAGE_SIZE, max=None)
|
|
34
33
|
|
|
35
34
|
|
|
36
|
-
class DataCrunchCompute(
|
|
35
|
+
class DataCrunchCompute(
|
|
36
|
+
ComputeWithCreateInstanceSupport,
|
|
37
|
+
Compute,
|
|
38
|
+
):
|
|
37
39
|
def __init__(self, config: DataCrunchConfig):
|
|
38
40
|
super().__init__()
|
|
39
41
|
self.config = config
|
|
@@ -148,25 +150,6 @@ class DataCrunchCompute(Compute):
|
|
|
148
150
|
backend_data=None,
|
|
149
151
|
)
|
|
150
152
|
|
|
151
|
-
def run_job(
|
|
152
|
-
self,
|
|
153
|
-
run: Run,
|
|
154
|
-
job: Job,
|
|
155
|
-
instance_offer: InstanceOfferWithAvailability,
|
|
156
|
-
project_ssh_public_key: str,
|
|
157
|
-
project_ssh_private_key: str,
|
|
158
|
-
volumes: List[Volume],
|
|
159
|
-
) -> JobProvisioningData:
|
|
160
|
-
instance_config = InstanceConfiguration(
|
|
161
|
-
project_name=run.project_name,
|
|
162
|
-
instance_name=job.job_spec.job_name, # TODO: generate name
|
|
163
|
-
ssh_keys=[
|
|
164
|
-
SSHKey(public=project_ssh_public_key.strip()),
|
|
165
|
-
],
|
|
166
|
-
user=run.user,
|
|
167
|
-
)
|
|
168
|
-
return self.create_instance(instance_offer, instance_config)
|
|
169
|
-
|
|
170
153
|
def terminate_instance(
|
|
171
154
|
self, instance_id: str, region: str, backend_data: Optional[str] = None
|
|
172
155
|
) -> None:
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
from dstack._internal.core.backends.base.configurator import (
|
|
4
|
+
BackendRecord,
|
|
5
|
+
Configurator,
|
|
6
|
+
)
|
|
7
|
+
from dstack._internal.core.backends.datacrunch.backend import DataCrunchBackend
|
|
8
|
+
from dstack._internal.core.backends.datacrunch.models import (
|
|
9
|
+
AnyDataCrunchBackendConfig,
|
|
10
|
+
DataCrunchBackendConfig,
|
|
11
|
+
DataCrunchBackendConfigWithCreds,
|
|
12
|
+
DataCrunchConfig,
|
|
13
|
+
DataCrunchCreds,
|
|
14
|
+
DataCrunchStoredConfig,
|
|
15
|
+
)
|
|
16
|
+
from dstack._internal.core.models.backends.base import (
|
|
17
|
+
BackendType,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
REGIONS = [
|
|
21
|
+
"FIN-01",
|
|
22
|
+
"ICE-01",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
DEFAULT_REGION = "FIN-01"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class DataCrunchConfigurator(Configurator):
|
|
29
|
+
TYPE = BackendType.DATACRUNCH
|
|
30
|
+
BACKEND_CLASS = DataCrunchBackend
|
|
31
|
+
|
|
32
|
+
def validate_config(
|
|
33
|
+
self, config: DataCrunchBackendConfigWithCreds, default_creds_enabled: bool
|
|
34
|
+
):
|
|
35
|
+
# FIXME: validate datacrunch creds
|
|
36
|
+
return
|
|
37
|
+
|
|
38
|
+
def create_backend(
|
|
39
|
+
self, project_name: str, config: DataCrunchBackendConfigWithCreds
|
|
40
|
+
) -> BackendRecord:
|
|
41
|
+
if config.regions is None:
|
|
42
|
+
config.regions = REGIONS
|
|
43
|
+
return BackendRecord(
|
|
44
|
+
config=DataCrunchStoredConfig(
|
|
45
|
+
**DataCrunchBackendConfig.__response__.parse_obj(config).dict()
|
|
46
|
+
).json(),
|
|
47
|
+
auth=DataCrunchCreds.parse_obj(config.creds).json(),
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
def get_backend_config(
|
|
51
|
+
self, record: BackendRecord, include_creds: bool
|
|
52
|
+
) -> AnyDataCrunchBackendConfig:
|
|
53
|
+
config = self._get_config(record)
|
|
54
|
+
if include_creds:
|
|
55
|
+
return DataCrunchBackendConfigWithCreds.__response__.parse_obj(config)
|
|
56
|
+
return DataCrunchBackendConfig.__response__.parse_obj(config)
|
|
57
|
+
|
|
58
|
+
def get_backend(self, record: BackendRecord) -> DataCrunchBackend:
|
|
59
|
+
config = self._get_config(record)
|
|
60
|
+
return DataCrunchBackend(config=config)
|
|
61
|
+
|
|
62
|
+
def _get_config(self, record: BackendRecord) -> DataCrunchConfig:
|
|
63
|
+
return DataCrunchConfig.__response__(
|
|
64
|
+
**json.loads(record.config),
|
|
65
|
+
creds=DataCrunchCreds.parse_raw(record.auth),
|
|
66
|
+
)
|
|
@@ -0,0 +1,38 @@
|
|
|
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 DataCrunchAPIKeyCreds(CoreModel):
|
|
9
|
+
type: Annotated[Literal["api_key"], Field(description="The type of credentials")] = "api_key"
|
|
10
|
+
client_id: Annotated[str, Field(description="The client ID")]
|
|
11
|
+
client_secret: Annotated[str, Field(description="The client secret")]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
AnyDataCrunchCreds = DataCrunchAPIKeyCreds
|
|
15
|
+
DataCrunchCreds = AnyDataCrunchCreds
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class DataCrunchBackendConfig(CoreModel):
|
|
19
|
+
type: Annotated[Literal["datacrunch"], Field(description="The type of backend")] = "datacrunch"
|
|
20
|
+
regions: Annotated[
|
|
21
|
+
Optional[List[str]],
|
|
22
|
+
Field(description="The list of DataCrunch regions. Omit to use all regions"),
|
|
23
|
+
] = None
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class DataCrunchBackendConfigWithCreds(DataCrunchBackendConfig):
|
|
27
|
+
creds: Annotated[AnyDataCrunchCreds, Field(description="The credentials")]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
AnyDataCrunchBackendConfig = Union[DataCrunchBackendConfig, DataCrunchBackendConfigWithCreds]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class DataCrunchStoredConfig(DataCrunchBackendConfig):
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class DataCrunchConfig(DataCrunchStoredConfig):
|
|
38
|
+
creds: AnyDataCrunchCreds
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
from typing import List
|
|
1
|
+
from typing import Annotated, List, Literal
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from pydantic import Field
|
|
4
4
|
|
|
5
5
|
from dstack._internal.core.models.common import CoreModel
|
|
6
6
|
|
|
7
|
-
# The OSS is currently aware of some of the DstackBackend internals (
|
|
7
|
+
# The OSS is currently aware of some of the DstackBackend internals (DstackBackendConfig) to be able to
|
|
8
8
|
# show DstackBackend base backends as regular backends.
|
|
9
9
|
# Consider designing an API that would allow DstackBackend to do the same without exposing its internals.
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
class
|
|
12
|
+
class DstackBackendConfig(CoreModel):
|
|
13
13
|
"""
|
|
14
14
|
This is a config model of DstackBackend stored in BackendModel.config and used by DstackConfigurator.
|
|
15
15
|
"""
|
|
@@ -18,9 +18,9 @@ class DstackConfigInfo(CoreModel):
|
|
|
18
18
|
base_backends: List[str]
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
class
|
|
21
|
+
class DstackBaseBackendConfig(CoreModel):
|
|
22
22
|
type: str
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
class
|
|
26
|
-
type: Literal["dstack"] = "dstack"
|
|
25
|
+
class DstackConfig(CoreModel):
|
|
26
|
+
type: Annotated[Literal["dstack"], Field(description="The type of backend")] = "dstack"
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
from dstack._internal.core.backends.base import Backend
|
|
2
|
-
from dstack._internal.core.backends.gcp.compute import GCPCompute
|
|
3
|
-
from dstack._internal.core.backends.gcp.config import GCPConfig
|
|
4
|
-
from dstack._internal.core.models.backends.base import BackendType
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
class GCPBackend(Backend):
|
|
8
|
-
TYPE: BackendType = BackendType.GCP
|
|
9
|
-
|
|
10
|
-
def __init__(self, config: GCPConfig):
|
|
11
|
-
self.config = config
|
|
12
|
-
self._compute = GCPCompute(self.config)
|
|
13
|
-
# self._check_credentials()
|
|
14
|
-
|
|
15
|
-
def compute(self) -> GCPCompute:
|
|
16
|
-
return self._compute
|
|
@@ -8,12 +8,11 @@ from google.auth.credentials import Credentials
|
|
|
8
8
|
from google.auth.exceptions import DefaultCredentialsError
|
|
9
9
|
from google.oauth2 import service_account
|
|
10
10
|
|
|
11
|
-
from dstack._internal.core.
|
|
12
|
-
from dstack._internal.core.models.backends.gcp import (
|
|
11
|
+
from dstack._internal.core.backends.gcp.models import (
|
|
13
12
|
AnyGCPCreds,
|
|
14
|
-
GCPDefaultCreds,
|
|
15
13
|
GCPServiceAccountCreds,
|
|
16
14
|
)
|
|
15
|
+
from dstack._internal.core.errors import BackendAuthError
|
|
17
16
|
from dstack._internal.core.models.common import is_core_model_instance
|
|
18
17
|
|
|
19
18
|
|
|
@@ -57,11 +56,3 @@ def validate_credentials(credentials: Credentials, project_id: str):
|
|
|
57
56
|
raise BackendAuthError(f"project_id {project_id} not found")
|
|
58
57
|
except Exception:
|
|
59
58
|
raise BackendAuthError("Insufficient permissions")
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
def default_creds_available() -> bool:
|
|
63
|
-
try:
|
|
64
|
-
authenticate(GCPDefaultCreds())
|
|
65
|
-
except BackendAuthError:
|
|
66
|
-
return False
|
|
67
|
-
return True
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from dstack._internal.core.backends.base.backend import Backend
|
|
2
|
+
from dstack._internal.core.backends.gcp.compute import GCPCompute
|
|
3
|
+
from dstack._internal.core.backends.gcp.models import GCPConfig
|
|
4
|
+
from dstack._internal.core.models.backends.base import BackendType
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class GCPBackend(Backend):
|
|
8
|
+
TYPE = BackendType.GCP
|
|
9
|
+
COMPUTE_CLASS = GCPCompute
|
|
10
|
+
|
|
11
|
+
def __init__(self, config: GCPConfig):
|
|
12
|
+
self.config = config
|
|
13
|
+
self._compute = GCPCompute(self.config)
|
|
14
|
+
# self._check_credentials()
|
|
15
|
+
|
|
16
|
+
def compute(self) -> GCPCompute:
|
|
17
|
+
return self._compute
|
|
@@ -12,17 +12,20 @@ import dstack._internal.core.backends.gcp.auth as auth
|
|
|
12
12
|
import dstack._internal.core.backends.gcp.resources as gcp_resources
|
|
13
13
|
from dstack._internal.core.backends.base.compute import (
|
|
14
14
|
Compute,
|
|
15
|
+
ComputeWithCreateInstanceSupport,
|
|
16
|
+
ComputeWithGatewaySupport,
|
|
17
|
+
ComputeWithMultinodeSupport,
|
|
18
|
+
ComputeWithVolumeSupport,
|
|
15
19
|
generate_unique_gateway_instance_name,
|
|
16
20
|
generate_unique_instance_name,
|
|
17
21
|
generate_unique_volume_name,
|
|
18
22
|
get_gateway_user_data,
|
|
19
|
-
get_job_instance_name,
|
|
20
23
|
get_shim_commands,
|
|
21
24
|
get_user_data,
|
|
22
25
|
merge_tags,
|
|
23
26
|
)
|
|
24
27
|
from dstack._internal.core.backends.base.offers import get_catalog_offers
|
|
25
|
-
from dstack._internal.core.backends.gcp.
|
|
28
|
+
from dstack._internal.core.backends.gcp.models import GCPConfig
|
|
26
29
|
from dstack._internal.core.errors import (
|
|
27
30
|
ComputeError,
|
|
28
31
|
ComputeResourceNotFoundError,
|
|
@@ -42,10 +45,9 @@ from dstack._internal.core.models.instances import (
|
|
|
42
45
|
InstanceOfferWithAvailability,
|
|
43
46
|
InstanceType,
|
|
44
47
|
Resources,
|
|
45
|
-
SSHKey,
|
|
46
48
|
)
|
|
47
49
|
from dstack._internal.core.models.resources import Memory, Range
|
|
48
|
-
from dstack._internal.core.models.runs import
|
|
50
|
+
from dstack._internal.core.models.runs import JobProvisioningData, Requirements
|
|
49
51
|
from dstack._internal.core.models.volumes import (
|
|
50
52
|
Volume,
|
|
51
53
|
VolumeAttachmentData,
|
|
@@ -69,7 +71,13 @@ class GCPVolumeDiskBackendData(CoreModel):
|
|
|
69
71
|
disk_type: str
|
|
70
72
|
|
|
71
73
|
|
|
72
|
-
class GCPCompute(
|
|
74
|
+
class GCPCompute(
|
|
75
|
+
ComputeWithCreateInstanceSupport,
|
|
76
|
+
ComputeWithMultinodeSupport,
|
|
77
|
+
ComputeWithGatewaySupport,
|
|
78
|
+
ComputeWithVolumeSupport,
|
|
79
|
+
Compute,
|
|
80
|
+
):
|
|
73
81
|
def __init__(self, config: GCPConfig):
|
|
74
82
|
super().__init__()
|
|
75
83
|
self.config = config
|
|
@@ -363,44 +371,6 @@ class GCPCompute(Compute):
|
|
|
363
371
|
f"Failed to get instance IP address. Instance status: {instance.status}"
|
|
364
372
|
)
|
|
365
373
|
|
|
366
|
-
def run_job(
|
|
367
|
-
self,
|
|
368
|
-
run: Run,
|
|
369
|
-
job: Job,
|
|
370
|
-
instance_offer: InstanceOfferWithAvailability,
|
|
371
|
-
project_ssh_public_key: str,
|
|
372
|
-
project_ssh_private_key: str,
|
|
373
|
-
volumes: List[Volume],
|
|
374
|
-
) -> JobProvisioningData:
|
|
375
|
-
# TODO: run_job is the same for vm-based backends, refactor
|
|
376
|
-
instance_config = InstanceConfiguration(
|
|
377
|
-
project_name=run.project_name,
|
|
378
|
-
instance_name=get_job_instance_name(run, job), # TODO: generate name
|
|
379
|
-
ssh_keys=[
|
|
380
|
-
SSHKey(public=project_ssh_public_key.strip()),
|
|
381
|
-
],
|
|
382
|
-
user=run.user,
|
|
383
|
-
volumes=volumes,
|
|
384
|
-
reservation=run.run_spec.configuration.reservation,
|
|
385
|
-
)
|
|
386
|
-
instance_offer = instance_offer.copy()
|
|
387
|
-
if len(volumes) > 0:
|
|
388
|
-
volume = volumes[0]
|
|
389
|
-
if (
|
|
390
|
-
volume.provisioning_data is not None
|
|
391
|
-
and volume.provisioning_data.availability_zone is not None
|
|
392
|
-
):
|
|
393
|
-
if instance_offer.availability_zones is None:
|
|
394
|
-
instance_offer.availability_zones = [
|
|
395
|
-
volume.provisioning_data.availability_zone
|
|
396
|
-
]
|
|
397
|
-
instance_offer.availability_zones = [
|
|
398
|
-
z
|
|
399
|
-
for z in instance_offer.availability_zones
|
|
400
|
-
if z == volume.provisioning_data.availability_zone
|
|
401
|
-
]
|
|
402
|
-
return self.create_instance(instance_offer, instance_config)
|
|
403
|
-
|
|
404
374
|
def create_gateway(
|
|
405
375
|
self,
|
|
406
376
|
configuration: GatewayComputeConfiguration,
|
|
@@ -580,7 +550,7 @@ class GCPCompute(Compute):
|
|
|
580
550
|
operation = self.disk_client.delete(
|
|
581
551
|
project=self.config.project_id,
|
|
582
552
|
zone=get_or_error(volume.provisioning_data).availability_zone,
|
|
583
|
-
disk=volume.
|
|
553
|
+
disk=volume.volume_id,
|
|
584
554
|
)
|
|
585
555
|
gcp_resources.wait_for_extended_operation(operation, "persistent disk deletion")
|
|
586
556
|
except google.api_core.exceptions.NotFound:
|