dstack 0.18.44__py3-none-any.whl → 0.19.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of dstack might be problematic. Click here for more details.
- dstack/_internal/cli/commands/gateway.py +15 -3
- dstack/_internal/cli/commands/logs.py +0 -22
- dstack/_internal/cli/commands/stats.py +8 -17
- dstack/_internal/cli/main.py +1 -5
- dstack/_internal/cli/services/configurators/fleet.py +4 -39
- dstack/_internal/cli/services/configurators/run.py +22 -21
- dstack/_internal/cli/services/profile.py +34 -83
- dstack/_internal/cli/utils/gateway.py +1 -1
- dstack/_internal/core/backends/__init__.py +56 -39
- dstack/_internal/core/backends/aws/__init__.py +0 -25
- dstack/_internal/core/backends/aws/auth.py +1 -10
- dstack/_internal/core/backends/aws/backend.py +26 -0
- dstack/_internal/core/backends/aws/compute.py +20 -45
- dstack/_internal/{server/services/backends/configurators/aws.py → core/backends/aws/configurator.py} +46 -85
- dstack/_internal/core/backends/aws/models.py +135 -0
- dstack/_internal/core/backends/aws/resources.py +1 -1
- dstack/_internal/core/backends/azure/__init__.py +0 -20
- dstack/_internal/core/backends/azure/auth.py +2 -11
- dstack/_internal/core/backends/azure/backend.py +21 -0
- dstack/_internal/core/backends/azure/compute.py +13 -27
- dstack/_internal/{server/services/backends/configurators/azure.py → core/backends/azure/configurator.py} +141 -210
- dstack/_internal/core/backends/azure/models.py +89 -0
- dstack/_internal/core/backends/base/__init__.py +0 -12
- dstack/_internal/core/backends/base/backend.py +18 -0
- dstack/_internal/core/backends/base/compute.py +153 -33
- dstack/_internal/core/backends/base/configurator.py +105 -0
- dstack/_internal/core/backends/base/models.py +14 -0
- dstack/_internal/core/backends/configurators.py +138 -0
- dstack/_internal/core/backends/cudo/__init__.py +0 -15
- dstack/_internal/core/backends/cudo/backend.py +16 -0
- dstack/_internal/core/backends/cudo/compute.py +8 -26
- dstack/_internal/core/backends/cudo/configurator.py +72 -0
- dstack/_internal/core/backends/cudo/models.py +37 -0
- dstack/_internal/core/backends/datacrunch/__init__.py +0 -15
- dstack/_internal/core/backends/datacrunch/backend.py +16 -0
- dstack/_internal/core/backends/datacrunch/compute.py +8 -25
- dstack/_internal/core/backends/datacrunch/configurator.py +66 -0
- dstack/_internal/core/backends/datacrunch/models.py +38 -0
- dstack/_internal/core/{models/backends/dstack.py → backends/dstack/models.py} +7 -7
- dstack/_internal/core/backends/gcp/__init__.py +0 -16
- dstack/_internal/core/backends/gcp/auth.py +2 -11
- dstack/_internal/core/backends/gcp/backend.py +17 -0
- dstack/_internal/core/backends/gcp/compute.py +13 -43
- dstack/_internal/{server/services/backends/configurators/gcp.py → core/backends/gcp/configurator.py} +46 -103
- dstack/_internal/core/backends/gcp/models.py +125 -0
- dstack/_internal/core/backends/kubernetes/__init__.py +0 -15
- dstack/_internal/core/backends/kubernetes/backend.py +16 -0
- dstack/_internal/core/backends/kubernetes/compute.py +16 -5
- dstack/_internal/core/backends/kubernetes/configurator.py +55 -0
- dstack/_internal/core/backends/kubernetes/models.py +72 -0
- dstack/_internal/core/backends/lambdalabs/__init__.py +0 -16
- dstack/_internal/core/backends/lambdalabs/backend.py +17 -0
- dstack/_internal/core/backends/lambdalabs/compute.py +7 -28
- dstack/_internal/core/backends/lambdalabs/configurator.py +82 -0
- dstack/_internal/core/backends/lambdalabs/models.py +37 -0
- dstack/_internal/core/backends/local/__init__.py +0 -13
- dstack/_internal/core/backends/local/backend.py +14 -0
- dstack/_internal/core/backends/local/compute.py +16 -2
- dstack/_internal/core/backends/models.py +128 -0
- dstack/_internal/core/backends/oci/__init__.py +0 -15
- dstack/_internal/core/backends/oci/auth.py +1 -5
- dstack/_internal/core/backends/oci/backend.py +16 -0
- dstack/_internal/core/backends/oci/compute.py +9 -23
- dstack/_internal/{server/services/backends/configurators/oci.py → core/backends/oci/configurator.py} +40 -85
- dstack/_internal/core/{models/backends/oci.py → backends/oci/models.py} +24 -25
- dstack/_internal/core/backends/oci/region.py +1 -1
- dstack/_internal/core/backends/runpod/__init__.py +0 -15
- dstack/_internal/core/backends/runpod/backend.py +16 -0
- dstack/_internal/core/backends/runpod/compute.py +7 -3
- dstack/_internal/core/backends/runpod/configurator.py +59 -0
- dstack/_internal/core/backends/runpod/models.py +54 -0
- dstack/_internal/core/backends/template/__init__.py +0 -0
- dstack/_internal/core/backends/tensordock/__init__.py +0 -15
- dstack/_internal/core/backends/tensordock/backend.py +16 -0
- dstack/_internal/core/backends/tensordock/compute.py +8 -27
- dstack/_internal/core/backends/tensordock/configurator.py +68 -0
- dstack/_internal/core/backends/tensordock/models.py +38 -0
- dstack/_internal/core/backends/vastai/__init__.py +0 -15
- dstack/_internal/core/backends/vastai/backend.py +16 -0
- dstack/_internal/core/backends/vastai/compute.py +2 -2
- dstack/_internal/core/backends/vastai/configurator.py +66 -0
- dstack/_internal/core/backends/vastai/models.py +37 -0
- dstack/_internal/core/backends/vultr/__init__.py +0 -15
- dstack/_internal/core/backends/vultr/backend.py +16 -0
- dstack/_internal/core/backends/vultr/compute.py +10 -24
- dstack/_internal/core/backends/vultr/configurator.py +64 -0
- dstack/_internal/core/backends/vultr/models.py +34 -0
- dstack/_internal/core/models/backends/__init__.py +0 -184
- dstack/_internal/core/models/backends/base.py +0 -19
- dstack/_internal/core/models/configurations.py +20 -15
- dstack/_internal/core/models/envs.py +4 -3
- dstack/_internal/core/models/fleets.py +17 -22
- dstack/_internal/core/models/gateways.py +3 -3
- dstack/_internal/core/models/instances.py +24 -0
- dstack/_internal/core/models/profiles.py +41 -46
- dstack/_internal/core/models/projects.py +1 -1
- dstack/_internal/core/models/repos/base.py +0 -5
- dstack/_internal/core/models/repos/local.py +3 -3
- dstack/_internal/core/models/repos/remote.py +26 -12
- dstack/_internal/core/models/repos/virtual.py +1 -1
- dstack/_internal/core/models/resources.py +45 -76
- dstack/_internal/core/models/runs.py +17 -19
- dstack/_internal/core/models/volumes.py +1 -3
- dstack/_internal/core/services/profiles.py +7 -16
- dstack/_internal/core/services/repos.py +0 -4
- dstack/_internal/server/app.py +0 -3
- dstack/_internal/server/background/tasks/process_gateways.py +4 -8
- dstack/_internal/server/background/tasks/process_instances.py +14 -9
- dstack/_internal/server/background/tasks/process_metrics.py +1 -1
- dstack/_internal/server/background/tasks/process_placement_groups.py +4 -1
- dstack/_internal/server/background/tasks/process_prometheus_metrics.py +1 -1
- dstack/_internal/server/background/tasks/process_running_jobs.py +14 -5
- dstack/_internal/server/background/tasks/process_submitted_jobs.py +16 -37
- dstack/_internal/server/background/tasks/process_volumes.py +5 -2
- dstack/_internal/server/migrations/versions/7bc2586e8b9e_make_instancemodel_pool_id_optional.py +36 -0
- dstack/_internal/server/migrations/versions/bc8ca4a505c6_store_backendtype_as_string.py +171 -0
- dstack/_internal/server/models.py +48 -9
- dstack/_internal/server/routers/backends.py +14 -23
- dstack/_internal/server/routers/instances.py +3 -4
- dstack/_internal/server/routers/metrics.py +10 -8
- dstack/_internal/server/routers/prometheus.py +1 -1
- dstack/_internal/server/routers/repos.py +1 -2
- dstack/_internal/server/routers/runs.py +13 -59
- dstack/_internal/server/schemas/gateways.py +14 -23
- dstack/_internal/server/schemas/projects.py +7 -2
- dstack/_internal/server/schemas/repos.py +2 -38
- dstack/_internal/server/schemas/runner.py +1 -0
- dstack/_internal/server/schemas/runs.py +1 -24
- dstack/_internal/server/services/backends/__init__.py +85 -158
- dstack/_internal/server/services/config.py +52 -576
- dstack/_internal/server/services/fleets.py +8 -103
- dstack/_internal/server/services/gateways/__init__.py +12 -4
- dstack/_internal/server/services/{pools.py → instances.py} +22 -329
- dstack/_internal/server/services/jobs/__init__.py +9 -6
- dstack/_internal/server/services/jobs/configurators/base.py +16 -0
- dstack/_internal/server/services/jobs/configurators/dev.py +9 -1
- dstack/_internal/server/services/jobs/configurators/extensions/cursor.py +42 -0
- dstack/_internal/server/services/metrics.py +39 -13
- dstack/_internal/server/services/offers.py +1 -1
- dstack/_internal/server/services/projects.py +23 -14
- dstack/_internal/server/services/prometheus.py +176 -18
- dstack/_internal/server/services/runs.py +24 -16
- dstack/_internal/server/services/volumes.py +8 -4
- dstack/_internal/server/statics/index.html +1 -1
- dstack/_internal/server/statics/{main-4eb116b97819badd1e2c.js → main-4a0fe83e84574654e397.js} +18 -14
- dstack/_internal/server/statics/{main-4eb116b97819badd1e2c.js.map → main-4a0fe83e84574654e397.js.map} +1 -1
- dstack/_internal/server/testing/common.py +58 -32
- dstack/_internal/utils/json_schema.py +6 -0
- dstack/_internal/utils/ssh.py +2 -1
- dstack/api/__init__.py +4 -0
- dstack/api/_public/__init__.py +16 -20
- dstack/api/_public/backends.py +1 -1
- dstack/api/_public/repos.py +36 -36
- dstack/api/_public/runs.py +167 -83
- dstack/api/server/__init__.py +11 -13
- dstack/api/server/_backends.py +12 -16
- dstack/api/server/_fleets.py +15 -57
- dstack/api/server/_gateways.py +3 -14
- dstack/api/server/_repos.py +1 -4
- dstack/api/server/_runs.py +21 -100
- dstack/api/server/_volumes.py +10 -5
- dstack/version.py +1 -1
- {dstack-0.18.44.dist-info → dstack-0.19.0.dist-info}/METADATA +1 -1
- {dstack-0.18.44.dist-info → dstack-0.19.0.dist-info}/RECORD +218 -204
- tests/_internal/cli/services/configurators/test_profile.py +6 -6
- tests/_internal/core/backends/aws/test_configurator.py +35 -0
- tests/_internal/core/backends/aws/test_resources.py +1 -1
- tests/_internal/core/backends/azure/test_configurator.py +61 -0
- tests/_internal/core/backends/cudo/__init__.py +0 -0
- tests/_internal/core/backends/cudo/test_configurator.py +37 -0
- tests/_internal/core/backends/datacrunch/__init__.py +0 -0
- tests/_internal/core/backends/datacrunch/test_configurator.py +17 -0
- tests/_internal/core/backends/gcp/test_configurator.py +42 -0
- tests/_internal/core/backends/kubernetes/test_configurator.py +43 -0
- tests/_internal/core/backends/lambdalabs/__init__.py +0 -0
- tests/_internal/core/backends/lambdalabs/test_configurator.py +38 -0
- tests/_internal/core/backends/oci/test_configurator.py +55 -0
- tests/_internal/core/backends/runpod/__init__.py +0 -0
- tests/_internal/core/backends/runpod/test_configurator.py +33 -0
- tests/_internal/core/backends/tensordock/__init__.py +0 -0
- tests/_internal/core/backends/tensordock/test_configurator.py +38 -0
- tests/_internal/core/backends/vastai/__init__.py +0 -0
- tests/_internal/core/backends/vastai/test_configurator.py +33 -0
- tests/_internal/core/backends/vultr/__init__.py +0 -0
- tests/_internal/core/backends/vultr/test_configurator.py +33 -0
- tests/_internal/server/background/tasks/test_process_gateways.py +4 -0
- tests/_internal/server/background/tasks/test_process_instances.py +49 -48
- tests/_internal/server/background/tasks/test_process_metrics.py +0 -3
- tests/_internal/server/background/tasks/test_process_placement_groups.py +2 -0
- tests/_internal/server/background/tasks/test_process_prometheus_metrics.py +0 -3
- tests/_internal/server/background/tasks/test_process_running_jobs.py +0 -21
- tests/_internal/server/background/tasks/test_process_runs.py +8 -22
- tests/_internal/server/background/tasks/test_process_submitted_jobs.py +3 -40
- tests/_internal/server/background/tasks/test_process_submitted_volumes.py +2 -0
- tests/_internal/server/background/tasks/test_process_terminating_jobs.py +10 -15
- tests/_internal/server/routers/test_backends.py +6 -764
- tests/_internal/server/routers/test_fleets.py +0 -26
- tests/_internal/server/routers/test_gateways.py +27 -3
- tests/_internal/server/routers/test_instances.py +0 -10
- tests/_internal/server/routers/test_metrics.py +27 -0
- tests/_internal/server/routers/test_projects.py +56 -0
- tests/_internal/server/routers/test_prometheus.py +116 -27
- tests/_internal/server/routers/test_repos.py +0 -15
- tests/_internal/server/routers/test_runs.py +4 -219
- tests/_internal/server/routers/test_volumes.py +2 -3
- tests/_internal/server/services/backends/__init__.py +0 -0
- tests/_internal/server/services/jobs/configurators/test_task.py +35 -0
- tests/_internal/server/services/test_config.py +7 -4
- tests/_internal/server/services/test_fleets.py +1 -4
- tests/_internal/server/services/{test_pools.py → test_instances.py} +11 -49
- tests/_internal/server/services/test_metrics.py +9 -5
- tests/_internal/server/services/test_repos.py +1 -14
- tests/_internal/server/services/test_runs.py +0 -4
- dstack/_internal/cli/commands/pool.py +0 -581
- dstack/_internal/cli/commands/run.py +0 -75
- dstack/_internal/core/backends/aws/config.py +0 -18
- dstack/_internal/core/backends/azure/config.py +0 -12
- dstack/_internal/core/backends/base/config.py +0 -5
- dstack/_internal/core/backends/cudo/config.py +0 -9
- dstack/_internal/core/backends/datacrunch/config.py +0 -9
- dstack/_internal/core/backends/gcp/config.py +0 -22
- dstack/_internal/core/backends/kubernetes/config.py +0 -6
- dstack/_internal/core/backends/lambdalabs/config.py +0 -9
- dstack/_internal/core/backends/nebius/__init__.py +0 -15
- dstack/_internal/core/backends/nebius/api_client.py +0 -319
- dstack/_internal/core/backends/nebius/compute.py +0 -220
- dstack/_internal/core/backends/nebius/config.py +0 -6
- dstack/_internal/core/backends/nebius/types.py +0 -37
- dstack/_internal/core/backends/oci/config.py +0 -6
- dstack/_internal/core/backends/runpod/config.py +0 -17
- dstack/_internal/core/backends/tensordock/config.py +0 -9
- dstack/_internal/core/backends/vastai/config.py +0 -6
- dstack/_internal/core/backends/vultr/config.py +0 -9
- dstack/_internal/core/models/backends/aws.py +0 -86
- dstack/_internal/core/models/backends/azure.py +0 -68
- dstack/_internal/core/models/backends/cudo.py +0 -43
- dstack/_internal/core/models/backends/datacrunch.py +0 -44
- dstack/_internal/core/models/backends/gcp.py +0 -67
- dstack/_internal/core/models/backends/kubernetes.py +0 -40
- dstack/_internal/core/models/backends/lambdalabs.py +0 -43
- dstack/_internal/core/models/backends/nebius.py +0 -54
- dstack/_internal/core/models/backends/runpod.py +0 -42
- dstack/_internal/core/models/backends/tensordock.py +0 -44
- dstack/_internal/core/models/backends/vastai.py +0 -43
- dstack/_internal/core/models/backends/vultr.py +0 -40
- dstack/_internal/core/models/pools.py +0 -43
- dstack/_internal/server/routers/pools.py +0 -142
- dstack/_internal/server/schemas/pools.py +0 -38
- dstack/_internal/server/services/backends/configurators/base.py +0 -72
- dstack/_internal/server/services/backends/configurators/cudo.py +0 -87
- dstack/_internal/server/services/backends/configurators/datacrunch.py +0 -79
- dstack/_internal/server/services/backends/configurators/kubernetes.py +0 -63
- dstack/_internal/server/services/backends/configurators/lambdalabs.py +0 -98
- dstack/_internal/server/services/backends/configurators/nebius.py +0 -85
- dstack/_internal/server/services/backends/configurators/runpod.py +0 -67
- dstack/_internal/server/services/backends/configurators/tensordock.py +0 -82
- dstack/_internal/server/services/backends/configurators/vastai.py +0 -80
- dstack/_internal/server/services/backends/configurators/vultr.py +0 -80
- dstack/api/_public/pools.py +0 -41
- dstack/api/_public/resources.py +0 -105
- dstack/api/server/_pools.py +0 -63
- tests/_internal/server/routers/test_pools.py +0 -612
- /dstack/_internal/{server/services/backends/configurators → core/backends/dstack}/__init__.py +0 -0
- {dstack-0.18.44.dist-info → dstack-0.19.0.dist-info}/LICENSE.md +0 -0
- {dstack-0.18.44.dist-info → dstack-0.19.0.dist-info}/WHEEL +0 -0
- {dstack-0.18.44.dist-info → dstack-0.19.0.dist-info}/entry_points.txt +0 -0
- {dstack-0.18.44.dist-info → dstack-0.19.0.dist-info}/top_level.txt +0 -0
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
from typing import List, Tuple
|
|
2
|
-
|
|
3
|
-
from fastapi import APIRouter, Depends
|
|
4
|
-
from sqlalchemy.ext.asyncio import AsyncSession
|
|
5
|
-
|
|
6
|
-
import dstack._internal.core.models.pools as models
|
|
7
|
-
import dstack._internal.server.schemas.pools as schemas
|
|
8
|
-
import dstack._internal.server.services.pools as pools
|
|
9
|
-
from dstack._internal.core.errors import ConfigurationError
|
|
10
|
-
from dstack._internal.core.models.pools import Instance
|
|
11
|
-
from dstack._internal.server.db import get_session
|
|
12
|
-
from dstack._internal.server.models import ProjectModel, UserModel
|
|
13
|
-
from dstack._internal.server.schemas.pools import ListPoolsRequest
|
|
14
|
-
from dstack._internal.server.schemas.runs import AddRemoteInstanceRequest
|
|
15
|
-
from dstack._internal.server.security.permissions import Authenticated, ProjectMember
|
|
16
|
-
from dstack._internal.server.utils.routers import get_base_api_additional_responses
|
|
17
|
-
|
|
18
|
-
root_router = APIRouter(
|
|
19
|
-
prefix="/api/pools",
|
|
20
|
-
tags=["pool"],
|
|
21
|
-
responses=get_base_api_additional_responses(),
|
|
22
|
-
deprecated=True,
|
|
23
|
-
)
|
|
24
|
-
router = APIRouter(
|
|
25
|
-
prefix="/api/project/{project_name}/pool",
|
|
26
|
-
tags=["pool"],
|
|
27
|
-
responses=get_base_api_additional_responses(),
|
|
28
|
-
deprecated=True,
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
@root_router.post("/list_instances")
|
|
33
|
-
async def list_pool_instances(
|
|
34
|
-
body: ListPoolsRequest,
|
|
35
|
-
session: AsyncSession = Depends(get_session),
|
|
36
|
-
user: UserModel = Depends(Authenticated()),
|
|
37
|
-
) -> List[Instance]:
|
|
38
|
-
"""
|
|
39
|
-
Returns all instances visible to user sorted by descending `created_at`.
|
|
40
|
-
`project_name` and `pool_name` can be specified as filters.
|
|
41
|
-
|
|
42
|
-
The results are paginated. To get the next page, pass `created_at` and `id` of
|
|
43
|
-
the last instance from the previous page as `prev_created_at` and `prev_id`.
|
|
44
|
-
"""
|
|
45
|
-
return await pools.list_user_pool_instances(
|
|
46
|
-
session=session,
|
|
47
|
-
user=user,
|
|
48
|
-
project_names=[body.project_name] if body.project_name is not None else None,
|
|
49
|
-
fleet_ids=None,
|
|
50
|
-
pool_name=body.pool_name,
|
|
51
|
-
only_active=body.only_active,
|
|
52
|
-
prev_created_at=body.prev_created_at,
|
|
53
|
-
prev_id=body.prev_id,
|
|
54
|
-
limit=body.limit,
|
|
55
|
-
ascending=body.ascending,
|
|
56
|
-
)
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
@router.post("/list")
|
|
60
|
-
async def list_pool(
|
|
61
|
-
session: AsyncSession = Depends(get_session),
|
|
62
|
-
user_project: Tuple[UserModel, ProjectModel] = Depends(ProjectMember()),
|
|
63
|
-
) -> List[models.Pool]:
|
|
64
|
-
_, project = user_project
|
|
65
|
-
return await pools.list_project_pools(session=session, project=project)
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
@router.post("/create")
|
|
69
|
-
async def create_pool(
|
|
70
|
-
body: schemas.CreatePoolRequest,
|
|
71
|
-
session: AsyncSession = Depends(get_session),
|
|
72
|
-
user_project: Tuple[UserModel, ProjectModel] = Depends(ProjectMember()),
|
|
73
|
-
) -> None:
|
|
74
|
-
_, project = user_project
|
|
75
|
-
await pools.create_pool(session=session, project=project, name=body.name)
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
@router.post("/set_default")
|
|
79
|
-
async def set_default_pool(
|
|
80
|
-
body: schemas.SetDefaultPoolRequest,
|
|
81
|
-
session: AsyncSession = Depends(get_session),
|
|
82
|
-
user_project: Tuple[UserModel, ProjectModel] = Depends(ProjectMember()),
|
|
83
|
-
):
|
|
84
|
-
_, project_model = user_project
|
|
85
|
-
await pools.set_default_pool(session, project_model, body.pool_name)
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
@router.post("/delete")
|
|
89
|
-
async def delete_pool(
|
|
90
|
-
body: schemas.DeletePoolRequest,
|
|
91
|
-
session: AsyncSession = Depends(get_session),
|
|
92
|
-
user_project: Tuple[UserModel, ProjectModel] = Depends(ProjectMember()),
|
|
93
|
-
) -> None:
|
|
94
|
-
_, project = user_project
|
|
95
|
-
await pools.delete_pool(session, project, body.name)
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
@router.post("/show")
|
|
99
|
-
async def show_pool(
|
|
100
|
-
body: schemas.ShowPoolRequest,
|
|
101
|
-
session: AsyncSession = Depends(get_session),
|
|
102
|
-
user_project: Tuple[UserModel, ProjectModel] = Depends(ProjectMember()),
|
|
103
|
-
) -> models.PoolInstances:
|
|
104
|
-
_, project = user_project
|
|
105
|
-
return await pools.show_pool_instances(session, project, pool_name=body.name)
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
@router.post("/remove")
|
|
109
|
-
async def remove_instance(
|
|
110
|
-
body: schemas.RemoveInstanceRequest,
|
|
111
|
-
session: AsyncSession = Depends(get_session),
|
|
112
|
-
user_project: Tuple[UserModel, ProjectModel] = Depends(ProjectMember()),
|
|
113
|
-
) -> None:
|
|
114
|
-
_, project_model = user_project
|
|
115
|
-
await pools.remove_instance(
|
|
116
|
-
session, project_model, body.pool_name, body.instance_name, body.force
|
|
117
|
-
)
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
@router.post("/add_remote")
|
|
121
|
-
async def add_instance(
|
|
122
|
-
body: AddRemoteInstanceRequest,
|
|
123
|
-
session: AsyncSession = Depends(get_session),
|
|
124
|
-
user_project: Tuple[UserModel, ProjectModel] = Depends(ProjectMember()),
|
|
125
|
-
) -> Instance:
|
|
126
|
-
if not body.host.strip() or not body.ssh_user.strip() or not body.ssh_keys:
|
|
127
|
-
raise ConfigurationError("Host, user or ssh keys are empty")
|
|
128
|
-
|
|
129
|
-
_, project = user_project
|
|
130
|
-
result = await pools.add_remote(
|
|
131
|
-
session,
|
|
132
|
-
project=project,
|
|
133
|
-
pool_name=body.pool_name,
|
|
134
|
-
instance_name=body.instance_name,
|
|
135
|
-
instance_network=body.instance_network,
|
|
136
|
-
region=body.region,
|
|
137
|
-
host=body.host,
|
|
138
|
-
port=body.port or 22,
|
|
139
|
-
ssh_user=body.ssh_user,
|
|
140
|
-
ssh_keys=body.ssh_keys,
|
|
141
|
-
)
|
|
142
|
-
return result
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
from datetime import datetime
|
|
2
|
-
from typing import Optional
|
|
3
|
-
from uuid import UUID
|
|
4
|
-
|
|
5
|
-
from dstack._internal.core.models.common import CoreModel
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class DeletePoolRequest(CoreModel):
|
|
9
|
-
name: str
|
|
10
|
-
force: bool
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class CreatePoolRequest(CoreModel):
|
|
14
|
-
name: str
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class ShowPoolRequest(CoreModel):
|
|
18
|
-
name: Optional[str]
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
class RemoveInstanceRequest(CoreModel):
|
|
22
|
-
pool_name: str
|
|
23
|
-
instance_name: str
|
|
24
|
-
force: bool = False
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
class SetDefaultPoolRequest(CoreModel):
|
|
28
|
-
pool_name: str
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
class ListPoolsRequest(CoreModel):
|
|
32
|
-
project_name: Optional[str]
|
|
33
|
-
pool_name: Optional[str]
|
|
34
|
-
only_active: bool = False
|
|
35
|
-
prev_created_at: Optional[datetime]
|
|
36
|
-
prev_id: Optional[UUID]
|
|
37
|
-
limit: int = 1000
|
|
38
|
-
ascending: bool = False
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
from abc import ABC, abstractmethod
|
|
2
|
-
from typing import Any, List, Optional
|
|
3
|
-
|
|
4
|
-
from dstack._internal.core.backends.base import Backend
|
|
5
|
-
from dstack._internal.core.errors import BackendInvalidCredentialsError
|
|
6
|
-
from dstack._internal.core.models.backends import (
|
|
7
|
-
AnyConfigInfo,
|
|
8
|
-
AnyConfigInfoWithCreds,
|
|
9
|
-
AnyConfigInfoWithCredsPartial,
|
|
10
|
-
AnyConfigValues,
|
|
11
|
-
)
|
|
12
|
-
from dstack._internal.core.models.backends.base import BackendType
|
|
13
|
-
from dstack._internal.server.models import BackendModel, ProjectModel
|
|
14
|
-
|
|
15
|
-
# Most clouds allow ~ 40-60 tags/labels per resource.
|
|
16
|
-
# We'll introduce our own base limit that can be customized per backend if required.
|
|
17
|
-
TAGS_MAX_NUM = 25
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
class Configurator(ABC):
|
|
21
|
-
TYPE: BackendType
|
|
22
|
-
|
|
23
|
-
def get_default_configs(self) -> List[AnyConfigInfoWithCreds]:
|
|
24
|
-
"""
|
|
25
|
-
Tries to detect backend creds on the machine and
|
|
26
|
-
automatically construct backend configs from the creds.
|
|
27
|
-
"""
|
|
28
|
-
return []
|
|
29
|
-
|
|
30
|
-
@abstractmethod
|
|
31
|
-
def get_config_values(self, config: AnyConfigInfoWithCredsPartial) -> AnyConfigValues:
|
|
32
|
-
"""
|
|
33
|
-
Validates backend config and returns possible values for unfilled config parameters.
|
|
34
|
-
"""
|
|
35
|
-
pass
|
|
36
|
-
|
|
37
|
-
@abstractmethod
|
|
38
|
-
def create_backend(
|
|
39
|
-
self, project: ProjectModel, config: AnyConfigInfoWithCreds
|
|
40
|
-
) -> BackendModel:
|
|
41
|
-
"""
|
|
42
|
-
Creates BackendModel given backend config and creds.
|
|
43
|
-
It may perform backend initialization, create
|
|
44
|
-
cloud resources such as networks and managed identites, and
|
|
45
|
-
save additional configuration parameters.
|
|
46
|
-
"""
|
|
47
|
-
pass
|
|
48
|
-
|
|
49
|
-
@abstractmethod
|
|
50
|
-
def get_config_info(self, model: BackendModel, include_creds: bool) -> AnyConfigInfo:
|
|
51
|
-
"""
|
|
52
|
-
Constructs backend's ConfigInfo to be returned in API responses.
|
|
53
|
-
Project admins may need to see backend's creds. In this case `include_creds` will be True.
|
|
54
|
-
Otherwise, no sensitive information should be included.
|
|
55
|
-
"""
|
|
56
|
-
pass
|
|
57
|
-
|
|
58
|
-
@abstractmethod
|
|
59
|
-
def get_backend(self, model: BackendModel) -> Backend:
|
|
60
|
-
"""
|
|
61
|
-
Returns Backend instance from config and creds stored in `model`.
|
|
62
|
-
"""
|
|
63
|
-
pass
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
def raise_invalid_credentials_error(
|
|
67
|
-
fields: Optional[List[List[str]]] = None, details: Optional[Any] = None
|
|
68
|
-
):
|
|
69
|
-
msg = BackendInvalidCredentialsError.msg
|
|
70
|
-
if details:
|
|
71
|
-
msg += f": {details}"
|
|
72
|
-
raise BackendInvalidCredentialsError(fields=fields, msg=msg)
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
from typing import List
|
|
3
|
-
|
|
4
|
-
from dstack._internal.core.backends.base import Backend
|
|
5
|
-
from dstack._internal.core.backends.cudo import CudoBackend, CudoConfig, api_client
|
|
6
|
-
from dstack._internal.core.models.backends.base import (
|
|
7
|
-
BackendType,
|
|
8
|
-
ConfigElementValue,
|
|
9
|
-
ConfigMultiElement,
|
|
10
|
-
)
|
|
11
|
-
from dstack._internal.core.models.backends.cudo import (
|
|
12
|
-
CudoConfigInfo,
|
|
13
|
-
CudoConfigInfoWithCreds,
|
|
14
|
-
CudoConfigInfoWithCredsPartial,
|
|
15
|
-
CudoConfigValues,
|
|
16
|
-
CudoCreds,
|
|
17
|
-
CudoStoredConfig,
|
|
18
|
-
)
|
|
19
|
-
from dstack._internal.server.models import BackendModel, DecryptedString, ProjectModel
|
|
20
|
-
from dstack._internal.server.services.backends import Configurator
|
|
21
|
-
from dstack._internal.server.services.backends.configurators.base import (
|
|
22
|
-
raise_invalid_credentials_error,
|
|
23
|
-
)
|
|
24
|
-
|
|
25
|
-
REGIONS = [
|
|
26
|
-
"no-luster-1",
|
|
27
|
-
"se-smedjebacken-1",
|
|
28
|
-
"gb-london-1",
|
|
29
|
-
"se-stockholm-1",
|
|
30
|
-
"us-newyork-1",
|
|
31
|
-
"us-santaclara-1",
|
|
32
|
-
]
|
|
33
|
-
|
|
34
|
-
DEFAULT_REGION = "no-luster-1"
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
class CudoConfigurator(Configurator):
|
|
38
|
-
TYPE: BackendType = BackendType.CUDO
|
|
39
|
-
|
|
40
|
-
def get_config_values(self, config: CudoConfigInfoWithCredsPartial) -> CudoConfigValues:
|
|
41
|
-
config_values = CudoConfigValues()
|
|
42
|
-
if config.creds is None:
|
|
43
|
-
return config_values
|
|
44
|
-
self._validate_cudo_api_key(config.creds.api_key)
|
|
45
|
-
config_values.regions = self._get_regions_element(
|
|
46
|
-
selected=config.regions or [DEFAULT_REGION]
|
|
47
|
-
)
|
|
48
|
-
return config_values
|
|
49
|
-
|
|
50
|
-
def create_backend(
|
|
51
|
-
self, project: ProjectModel, config: CudoConfigInfoWithCreds
|
|
52
|
-
) -> BackendModel:
|
|
53
|
-
if config.regions is None:
|
|
54
|
-
config.regions = REGIONS
|
|
55
|
-
return BackendModel(
|
|
56
|
-
project_id=project.id,
|
|
57
|
-
type=self.TYPE.value,
|
|
58
|
-
config=CudoStoredConfig(**CudoConfigInfo.__response__.parse_obj(config).dict()).json(),
|
|
59
|
-
auth=DecryptedString(plaintext=CudoCreds.parse_obj(config.creds).json()),
|
|
60
|
-
)
|
|
61
|
-
|
|
62
|
-
def get_config_info(self, model: BackendModel, include_creds: bool) -> CudoConfigInfo:
|
|
63
|
-
config = self._get_backend_config(model)
|
|
64
|
-
if include_creds:
|
|
65
|
-
return CudoConfigInfoWithCreds.__response__.parse_obj(config)
|
|
66
|
-
return CudoConfigInfo.__response__.parse_obj(config)
|
|
67
|
-
|
|
68
|
-
def get_backend(self, model: BackendModel) -> Backend:
|
|
69
|
-
config = self._get_backend_config(model)
|
|
70
|
-
return CudoBackend(config=config)
|
|
71
|
-
|
|
72
|
-
def _get_regions_element(self, selected: List[str]) -> ConfigMultiElement:
|
|
73
|
-
element = ConfigMultiElement(selected=selected)
|
|
74
|
-
for r in REGIONS:
|
|
75
|
-
element.values.append(ConfigElementValue(value=r, label=r))
|
|
76
|
-
return element
|
|
77
|
-
|
|
78
|
-
def _get_backend_config(self, model: BackendModel) -> CudoConfig:
|
|
79
|
-
return CudoConfig.__response__(
|
|
80
|
-
**json.loads(model.config),
|
|
81
|
-
creds=CudoCreds.parse_raw(model.auth.get_plaintext_or_error()),
|
|
82
|
-
)
|
|
83
|
-
|
|
84
|
-
def _validate_cudo_api_key(self, api_key: str):
|
|
85
|
-
client = api_client.CudoApiClient(api_key=api_key)
|
|
86
|
-
if not client.validate_api_key():
|
|
87
|
-
raise_invalid_credentials_error(fields=[["creds", "api_key"]])
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
from typing import List
|
|
3
|
-
|
|
4
|
-
from dstack._internal.core.backends.datacrunch import DataCrunchBackend
|
|
5
|
-
from dstack._internal.core.backends.datacrunch.config import DataCrunchConfig
|
|
6
|
-
from dstack._internal.core.models.backends.base import (
|
|
7
|
-
BackendType,
|
|
8
|
-
ConfigElementValue,
|
|
9
|
-
ConfigMultiElement,
|
|
10
|
-
)
|
|
11
|
-
from dstack._internal.core.models.backends.datacrunch import (
|
|
12
|
-
AnyDataCrunchConfigInfo,
|
|
13
|
-
DataCrunchConfigInfo,
|
|
14
|
-
DataCrunchConfigInfoWithCreds,
|
|
15
|
-
DataCrunchConfigInfoWithCredsPartial,
|
|
16
|
-
DataCrunchConfigValues,
|
|
17
|
-
DataCrunchCreds,
|
|
18
|
-
DataCrunchStoredConfig,
|
|
19
|
-
)
|
|
20
|
-
from dstack._internal.server.models import BackendModel, DecryptedString, ProjectModel
|
|
21
|
-
from dstack._internal.server.services.backends.configurators.base import Configurator
|
|
22
|
-
|
|
23
|
-
REGIONS = [
|
|
24
|
-
"FIN-01",
|
|
25
|
-
"ICE-01",
|
|
26
|
-
]
|
|
27
|
-
|
|
28
|
-
DEFAULT_REGION = "FIN-01"
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
class DataCrunchConfigurator(Configurator):
|
|
32
|
-
TYPE: BackendType = BackendType.DATACRUNCH
|
|
33
|
-
|
|
34
|
-
def get_config_values(
|
|
35
|
-
self, config: DataCrunchConfigInfoWithCredsPartial
|
|
36
|
-
) -> DataCrunchConfigValues:
|
|
37
|
-
config_values = DataCrunchConfigValues()
|
|
38
|
-
if config.creds is None:
|
|
39
|
-
return config_values
|
|
40
|
-
config_values.regions = self._get_regions_element(
|
|
41
|
-
selected=config.regions or [DEFAULT_REGION]
|
|
42
|
-
)
|
|
43
|
-
return config_values
|
|
44
|
-
|
|
45
|
-
def create_backend(
|
|
46
|
-
self, project: ProjectModel, config: DataCrunchConfigInfoWithCreds
|
|
47
|
-
) -> BackendModel:
|
|
48
|
-
if config.regions is None:
|
|
49
|
-
config.regions = REGIONS
|
|
50
|
-
return BackendModel(
|
|
51
|
-
project_id=project.id,
|
|
52
|
-
type=self.TYPE.value,
|
|
53
|
-
config=DataCrunchStoredConfig(
|
|
54
|
-
**DataCrunchConfigInfo.__response__.parse_obj(config).dict()
|
|
55
|
-
).json(),
|
|
56
|
-
auth=DecryptedString(plaintext=DataCrunchCreds.parse_obj(config.creds).json()),
|
|
57
|
-
)
|
|
58
|
-
|
|
59
|
-
def get_config_info(self, model: BackendModel, include_creds: bool) -> AnyDataCrunchConfigInfo:
|
|
60
|
-
config = self._get_backend_config(model)
|
|
61
|
-
if include_creds:
|
|
62
|
-
return DataCrunchConfigInfoWithCreds.__response__.parse_obj(config)
|
|
63
|
-
return DataCrunchConfigInfo.__response__.parse_obj(config)
|
|
64
|
-
|
|
65
|
-
def get_backend(self, model: BackendModel) -> DataCrunchBackend:
|
|
66
|
-
config = self._get_backend_config(model)
|
|
67
|
-
return DataCrunchBackend(config=config)
|
|
68
|
-
|
|
69
|
-
def _get_backend_config(self, model: BackendModel) -> DataCrunchConfig:
|
|
70
|
-
return DataCrunchConfig.__response__(
|
|
71
|
-
**json.loads(model.config),
|
|
72
|
-
creds=DataCrunchCreds.parse_raw(model.auth.get_plaintext_or_error()),
|
|
73
|
-
)
|
|
74
|
-
|
|
75
|
-
def _get_regions_element(self, selected: List[str]) -> ConfigMultiElement:
|
|
76
|
-
element = ConfigMultiElement(selected=selected)
|
|
77
|
-
for r in REGIONS:
|
|
78
|
-
element.values.append(ConfigElementValue(value=r, label=r))
|
|
79
|
-
return element
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
from typing import List
|
|
2
|
-
|
|
3
|
-
from dstack._internal.core.backends.kubernetes import KubernetesBackend
|
|
4
|
-
from dstack._internal.core.backends.kubernetes.config import KubernetesConfig
|
|
5
|
-
from dstack._internal.core.backends.kubernetes.utils import get_api_from_config_data
|
|
6
|
-
from dstack._internal.core.models.backends.base import BackendType
|
|
7
|
-
from dstack._internal.core.models.backends.kubernetes import (
|
|
8
|
-
AnyKubernetesConfigInfo,
|
|
9
|
-
KubernetesConfigInfo,
|
|
10
|
-
KubernetesConfigInfoWithCreds,
|
|
11
|
-
KubernetesConfigInfoWithCredsPartial,
|
|
12
|
-
KubernetesConfigValues,
|
|
13
|
-
KubernetesStoredConfig,
|
|
14
|
-
)
|
|
15
|
-
from dstack._internal.server.models import BackendModel, DecryptedString, ProjectModel
|
|
16
|
-
from dstack._internal.server.services.backends.configurators.base import (
|
|
17
|
-
Configurator,
|
|
18
|
-
raise_invalid_credentials_error,
|
|
19
|
-
)
|
|
20
|
-
from dstack._internal.utils.logging import get_logger
|
|
21
|
-
|
|
22
|
-
logger = get_logger(__name__)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
class KubernetesConfigurator(Configurator):
|
|
26
|
-
TYPE: BackendType = BackendType.KUBERNETES
|
|
27
|
-
|
|
28
|
-
def get_default_configs(self) -> List[KubernetesConfigInfoWithCreds]:
|
|
29
|
-
# TODO: automatically pick up kubernetes config
|
|
30
|
-
return []
|
|
31
|
-
|
|
32
|
-
def get_config_values(
|
|
33
|
-
self, config: KubernetesConfigInfoWithCredsPartial
|
|
34
|
-
) -> KubernetesConfigValues:
|
|
35
|
-
try:
|
|
36
|
-
api = get_api_from_config_data(config.kubeconfig.data)
|
|
37
|
-
api.list_node()
|
|
38
|
-
except Exception as e:
|
|
39
|
-
logger.debug("Invalid kubeconfig: %s", str(e))
|
|
40
|
-
raise_invalid_credentials_error(fields=[["kubeconfig"]])
|
|
41
|
-
return KubernetesConfigValues()
|
|
42
|
-
|
|
43
|
-
def create_backend(
|
|
44
|
-
self, project: ProjectModel, config: KubernetesConfigInfoWithCreds
|
|
45
|
-
) -> BackendModel:
|
|
46
|
-
return BackendModel(
|
|
47
|
-
project_id=project.id,
|
|
48
|
-
type=self.TYPE.value,
|
|
49
|
-
config=KubernetesStoredConfig.__response__.parse_obj(config).json(),
|
|
50
|
-
auth=DecryptedString(plaintext=""),
|
|
51
|
-
)
|
|
52
|
-
|
|
53
|
-
def get_config_info(self, model: BackendModel, include_creds: bool) -> AnyKubernetesConfigInfo:
|
|
54
|
-
config = self._get_backend_config(model)
|
|
55
|
-
if include_creds:
|
|
56
|
-
return KubernetesConfigInfoWithCreds.__response__.parse_obj(config)
|
|
57
|
-
return KubernetesConfigInfo.__response__.parse_obj(config)
|
|
58
|
-
|
|
59
|
-
def get_backend(self, model: BackendModel) -> KubernetesBackend:
|
|
60
|
-
return KubernetesBackend(self._get_backend_config(model))
|
|
61
|
-
|
|
62
|
-
def _get_backend_config(self, model: BackendModel) -> KubernetesConfig:
|
|
63
|
-
return KubernetesConfig.__response__.parse_raw(model.config)
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
from typing import List
|
|
3
|
-
|
|
4
|
-
from dstack._internal.core.backends.lambdalabs import LambdaBackend, api_client
|
|
5
|
-
from dstack._internal.core.backends.lambdalabs.config import LambdaConfig
|
|
6
|
-
from dstack._internal.core.models.backends.base import (
|
|
7
|
-
BackendType,
|
|
8
|
-
ConfigElementValue,
|
|
9
|
-
ConfigMultiElement,
|
|
10
|
-
)
|
|
11
|
-
from dstack._internal.core.models.backends.lambdalabs import (
|
|
12
|
-
AnyLambdaConfigInfo,
|
|
13
|
-
LambdaConfigInfo,
|
|
14
|
-
LambdaConfigInfoWithCreds,
|
|
15
|
-
LambdaConfigInfoWithCredsPartial,
|
|
16
|
-
LambdaConfigValues,
|
|
17
|
-
LambdaCreds,
|
|
18
|
-
LambdaStoredConfig,
|
|
19
|
-
)
|
|
20
|
-
from dstack._internal.server.models import BackendModel, DecryptedString, ProjectModel
|
|
21
|
-
from dstack._internal.server.services.backends.configurators.base import (
|
|
22
|
-
Configurator,
|
|
23
|
-
raise_invalid_credentials_error,
|
|
24
|
-
)
|
|
25
|
-
|
|
26
|
-
REGIONS = [
|
|
27
|
-
"us-south-1",
|
|
28
|
-
"us-south-2",
|
|
29
|
-
"us-south-3",
|
|
30
|
-
"us-west-2",
|
|
31
|
-
"us-west-1",
|
|
32
|
-
"us-midwest-1",
|
|
33
|
-
"us-west-3",
|
|
34
|
-
"us-east-1",
|
|
35
|
-
"us-east-2",
|
|
36
|
-
"europe-central-1",
|
|
37
|
-
"asia-south-1",
|
|
38
|
-
"me-west-1",
|
|
39
|
-
"asia-northeast-1",
|
|
40
|
-
"asia-northeast-2",
|
|
41
|
-
]
|
|
42
|
-
|
|
43
|
-
DEFAULT_REGION = "us-east-1"
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
class LambdaConfigurator(Configurator):
|
|
47
|
-
TYPE: BackendType = BackendType.LAMBDA
|
|
48
|
-
|
|
49
|
-
def get_config_values(self, config: LambdaConfigInfoWithCredsPartial) -> LambdaConfigValues:
|
|
50
|
-
config_values = LambdaConfigValues()
|
|
51
|
-
if config.creds is None:
|
|
52
|
-
return config_values
|
|
53
|
-
self._validate_lambda_api_key(config.creds.api_key)
|
|
54
|
-
config_values.regions = self._get_regions_element(
|
|
55
|
-
selected=config.regions or [DEFAULT_REGION]
|
|
56
|
-
)
|
|
57
|
-
return config_values
|
|
58
|
-
|
|
59
|
-
def create_backend(
|
|
60
|
-
self, project: ProjectModel, config: LambdaConfigInfoWithCreds
|
|
61
|
-
) -> BackendModel:
|
|
62
|
-
if config.regions is None:
|
|
63
|
-
config.regions = REGIONS
|
|
64
|
-
return BackendModel(
|
|
65
|
-
project_id=project.id,
|
|
66
|
-
type=self.TYPE.value,
|
|
67
|
-
config=LambdaStoredConfig(
|
|
68
|
-
**LambdaConfigInfo.__response__.parse_obj(config).dict()
|
|
69
|
-
).json(),
|
|
70
|
-
auth=DecryptedString(plaintext=LambdaCreds.parse_obj(config.creds).json()),
|
|
71
|
-
)
|
|
72
|
-
|
|
73
|
-
def get_config_info(self, model: BackendModel, include_creds: bool) -> AnyLambdaConfigInfo:
|
|
74
|
-
config = self._get_backend_config(model)
|
|
75
|
-
if include_creds:
|
|
76
|
-
return LambdaConfigInfoWithCreds.__response__.parse_obj(config)
|
|
77
|
-
return LambdaConfigInfo.__response__.parse_obj(config)
|
|
78
|
-
|
|
79
|
-
def get_backend(self, model: BackendModel) -> LambdaBackend:
|
|
80
|
-
config = self._get_backend_config(model)
|
|
81
|
-
return LambdaBackend(config=config)
|
|
82
|
-
|
|
83
|
-
def _get_backend_config(self, model: BackendModel) -> LambdaConfig:
|
|
84
|
-
return LambdaConfig.__response__(
|
|
85
|
-
**json.loads(model.config),
|
|
86
|
-
creds=LambdaCreds.parse_raw(model.auth.get_plaintext_or_error()),
|
|
87
|
-
)
|
|
88
|
-
|
|
89
|
-
def _validate_lambda_api_key(self, api_key: str):
|
|
90
|
-
client = api_client.LambdaAPIClient(api_key=api_key)
|
|
91
|
-
if not client.validate_api_key():
|
|
92
|
-
raise_invalid_credentials_error(fields=[["creds", "api_key"]])
|
|
93
|
-
|
|
94
|
-
def _get_regions_element(self, selected: List[str]) -> ConfigMultiElement:
|
|
95
|
-
element = ConfigMultiElement(selected=selected)
|
|
96
|
-
for r in REGIONS:
|
|
97
|
-
element.values.append(ConfigElementValue(value=r, label=r))
|
|
98
|
-
return element
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
from typing import List
|
|
3
|
-
|
|
4
|
-
import requests
|
|
5
|
-
|
|
6
|
-
import dstack._internal.core.backends.nebius.api_client as api_client
|
|
7
|
-
from dstack._internal.core.backends.base import Backend
|
|
8
|
-
from dstack._internal.core.backends.nebius import NebiusBackend
|
|
9
|
-
from dstack._internal.core.backends.nebius.config import NebiusConfig
|
|
10
|
-
from dstack._internal.core.models.backends.base import (
|
|
11
|
-
BackendType,
|
|
12
|
-
ConfigElementValue,
|
|
13
|
-
ConfigMultiElement,
|
|
14
|
-
)
|
|
15
|
-
from dstack._internal.core.models.backends.nebius import (
|
|
16
|
-
NebiusConfigInfo,
|
|
17
|
-
NebiusConfigInfoWithCreds,
|
|
18
|
-
NebiusConfigInfoWithCredsPartial,
|
|
19
|
-
NebiusConfigValues,
|
|
20
|
-
NebiusCreds,
|
|
21
|
-
NebiusStoredConfig,
|
|
22
|
-
)
|
|
23
|
-
from dstack._internal.server.models import BackendModel, DecryptedString, ProjectModel
|
|
24
|
-
from dstack._internal.server.services.backends import Configurator
|
|
25
|
-
from dstack._internal.server.services.backends.configurators.base import (
|
|
26
|
-
raise_invalid_credentials_error,
|
|
27
|
-
)
|
|
28
|
-
|
|
29
|
-
REGIONS = ["eu-north1-c"]
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
class NebiusConfigurator(Configurator):
|
|
33
|
-
TYPE: BackendType = BackendType.NEBIUS
|
|
34
|
-
|
|
35
|
-
def get_config_values(self, config: NebiusConfigInfoWithCredsPartial) -> NebiusConfigValues:
|
|
36
|
-
config_values = NebiusConfigValues()
|
|
37
|
-
if config.creds is None:
|
|
38
|
-
return config_values
|
|
39
|
-
self._validate_nebius_creds(config.creds)
|
|
40
|
-
# TODO(egor-s) cloud_id
|
|
41
|
-
# TODO(egor-s) folder_id
|
|
42
|
-
# TODO(egor-s) network_id
|
|
43
|
-
config_values.regions = self._get_regions_element(selected=config.regions or [])
|
|
44
|
-
return config_values
|
|
45
|
-
|
|
46
|
-
def create_backend(
|
|
47
|
-
self, project: ProjectModel, config: NebiusConfigInfoWithCreds
|
|
48
|
-
) -> BackendModel:
|
|
49
|
-
if config.regions is None:
|
|
50
|
-
config.regions = REGIONS
|
|
51
|
-
self._validate_nebius_creds(config.creds)
|
|
52
|
-
return BackendModel(
|
|
53
|
-
project_id=project.id,
|
|
54
|
-
type=self.TYPE.value,
|
|
55
|
-
config=NebiusStoredConfig.__response__.parse_obj(config).json(),
|
|
56
|
-
auth=DecryptedString(plaintext=NebiusCreds.parse_obj(config.creds).json()),
|
|
57
|
-
)
|
|
58
|
-
|
|
59
|
-
def get_config_info(self, model: BackendModel, include_creds: bool) -> NebiusConfigInfo:
|
|
60
|
-
config = self._get_backend_config(model)
|
|
61
|
-
if include_creds:
|
|
62
|
-
return NebiusConfigInfoWithCreds.__response__.parse_obj(config)
|
|
63
|
-
return NebiusConfigInfo.__response__.parse_obj(config)
|
|
64
|
-
|
|
65
|
-
def get_backend(self, model: BackendModel) -> Backend:
|
|
66
|
-
config = self._get_backend_config(model)
|
|
67
|
-
return NebiusBackend(config=config)
|
|
68
|
-
|
|
69
|
-
def _get_backend_config(self, model: BackendModel) -> NebiusConfig:
|
|
70
|
-
return NebiusConfig.__response__(
|
|
71
|
-
**json.loads(model.config),
|
|
72
|
-
creds=NebiusCreds.parse_raw(model.auth.get_plaintext_or_error()),
|
|
73
|
-
)
|
|
74
|
-
|
|
75
|
-
def _validate_nebius_creds(self, creds: NebiusCreds):
|
|
76
|
-
try:
|
|
77
|
-
api_client.NebiusAPIClient(json.loads(creds.data)).get_token()
|
|
78
|
-
except requests.HTTPError:
|
|
79
|
-
raise_invalid_credentials_error(fields=[["creds", "data"]])
|
|
80
|
-
|
|
81
|
-
def _get_regions_element(self, selected: List[str]) -> ConfigMultiElement:
|
|
82
|
-
element = ConfigMultiElement(selected=selected)
|
|
83
|
-
for r in REGIONS:
|
|
84
|
-
element.values.append(ConfigElementValue(value=r, label=r))
|
|
85
|
-
return element
|