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.
- 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-4fd5a4770eff59325ee3.js} +68 -15
- dstack/_internal/server/statics/{main-fe8fd9db55df8d10e648.js.map → main-4fd5a4770eff59325ee3.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.0rc1.dist-info}/METADATA +10 -1
- {dstack-0.18.43.dist-info → dstack-0.19.0rc1.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.0rc1.dist-info}/LICENSE.md +0 -0
- {dstack-0.18.43.dist-info → dstack-0.19.0rc1.dist-info}/WHEEL +0 -0
- {dstack-0.18.43.dist-info → dstack-0.19.0rc1.dist-info}/entry_points.txt +0 -0
- {dstack-0.18.43.dist-info → dstack-0.19.0rc1.dist-info}/top_level.txt +0 -0
|
@@ -3,15 +3,18 @@ import uuid
|
|
|
3
3
|
import pytest
|
|
4
4
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
5
5
|
|
|
6
|
-
import dstack._internal.server.services.
|
|
6
|
+
import dstack._internal.server.services.instances as instances_services
|
|
7
7
|
from dstack._internal.core.models.backends.base import BackendType
|
|
8
|
-
from dstack._internal.core.models.instances import
|
|
9
|
-
|
|
8
|
+
from dstack._internal.core.models.instances import (
|
|
9
|
+
Instance,
|
|
10
|
+
InstanceStatus,
|
|
11
|
+
InstanceType,
|
|
12
|
+
Resources,
|
|
13
|
+
)
|
|
10
14
|
from dstack._internal.core.models.profiles import Profile
|
|
11
15
|
from dstack._internal.server.models import InstanceModel
|
|
12
16
|
from dstack._internal.server.testing.common import (
|
|
13
17
|
create_instance,
|
|
14
|
-
create_pool,
|
|
15
18
|
create_project,
|
|
16
19
|
create_user,
|
|
17
20
|
get_volume,
|
|
@@ -27,21 +30,18 @@ class TestFilterPoolInstances:
|
|
|
27
30
|
async def test_returns_all_instances(self, test_db, session: AsyncSession):
|
|
28
31
|
user = await create_user(session=session)
|
|
29
32
|
project = await create_project(session=session, owner=user)
|
|
30
|
-
pool = await create_pool(session=session, project=project)
|
|
31
33
|
aws_instance = await create_instance(
|
|
32
34
|
session=session,
|
|
33
35
|
project=project,
|
|
34
|
-
pool=pool,
|
|
35
36
|
backend=BackendType.AWS,
|
|
36
37
|
)
|
|
37
38
|
runpod_instance = await create_instance(
|
|
38
39
|
session=session,
|
|
39
40
|
project=project,
|
|
40
|
-
pool=pool,
|
|
41
41
|
backend=BackendType.RUNPOD,
|
|
42
42
|
)
|
|
43
43
|
instances = [aws_instance, runpod_instance]
|
|
44
|
-
res =
|
|
44
|
+
res = instances_services.filter_pool_instances(
|
|
45
45
|
pool_instances=instances,
|
|
46
46
|
profile=Profile(name="test"),
|
|
47
47
|
)
|
|
@@ -52,21 +52,18 @@ class TestFilterPoolInstances:
|
|
|
52
52
|
async def test_returns_multinode_instances(self, test_db, session: AsyncSession):
|
|
53
53
|
user = await create_user(session=session)
|
|
54
54
|
project = await create_project(session=session, owner=user)
|
|
55
|
-
pool = await create_pool(session=session, project=project)
|
|
56
55
|
aws_instance = await create_instance(
|
|
57
56
|
session=session,
|
|
58
57
|
project=project,
|
|
59
|
-
pool=pool,
|
|
60
58
|
backend=BackendType.AWS,
|
|
61
59
|
)
|
|
62
60
|
runpod_instance = await create_instance(
|
|
63
61
|
session=session,
|
|
64
62
|
project=project,
|
|
65
|
-
pool=pool,
|
|
66
63
|
backend=BackendType.RUNPOD,
|
|
67
64
|
)
|
|
68
65
|
instances = [aws_instance, runpod_instance]
|
|
69
|
-
res =
|
|
66
|
+
res = instances_services.filter_pool_instances(
|
|
70
67
|
pool_instances=instances,
|
|
71
68
|
profile=Profile(name="test"),
|
|
72
69
|
multinode=True,
|
|
@@ -78,29 +75,25 @@ class TestFilterPoolInstances:
|
|
|
78
75
|
async def test_returns_volume_instances(self, test_db, session: AsyncSession):
|
|
79
76
|
user = await create_user(session=session)
|
|
80
77
|
project = await create_project(session=session, owner=user)
|
|
81
|
-
pool = await create_pool(session=session, project=project)
|
|
82
78
|
aws_instance = await create_instance(
|
|
83
79
|
session=session,
|
|
84
80
|
project=project,
|
|
85
|
-
pool=pool,
|
|
86
81
|
backend=BackendType.AWS,
|
|
87
82
|
)
|
|
88
83
|
runpod_instance1 = await create_instance(
|
|
89
84
|
session=session,
|
|
90
85
|
project=project,
|
|
91
|
-
pool=pool,
|
|
92
86
|
backend=BackendType.RUNPOD,
|
|
93
87
|
region="eu",
|
|
94
88
|
)
|
|
95
89
|
runpod_instance2 = await create_instance(
|
|
96
90
|
session=session,
|
|
97
91
|
project=project,
|
|
98
|
-
pool=pool,
|
|
99
92
|
backend=BackendType.RUNPOD,
|
|
100
93
|
region="us",
|
|
101
94
|
)
|
|
102
95
|
instances = [aws_instance, runpod_instance1, runpod_instance2]
|
|
103
|
-
res =
|
|
96
|
+
res = instances_services.filter_pool_instances(
|
|
104
97
|
pool_instances=instances,
|
|
105
98
|
profile=Profile(name="test"),
|
|
106
99
|
volumes=[
|
|
@@ -116,36 +109,6 @@ class TestFilterPoolInstances:
|
|
|
116
109
|
assert res == [runpod_instance2]
|
|
117
110
|
|
|
118
111
|
|
|
119
|
-
class TestGenerateInstanceName:
|
|
120
|
-
@pytest.mark.asyncio
|
|
121
|
-
@pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True)
|
|
122
|
-
async def test_generates_instance_name(self, test_db, session: AsyncSession):
|
|
123
|
-
user = await create_user(session=session)
|
|
124
|
-
project = await create_project(session=session, owner=user)
|
|
125
|
-
pool = await services_pools.create_pool(session=session, project=project, name="test_pool")
|
|
126
|
-
im = InstanceModel(
|
|
127
|
-
name="test_instnce",
|
|
128
|
-
project=project,
|
|
129
|
-
pool=pool,
|
|
130
|
-
status=InstanceStatus.PENDING,
|
|
131
|
-
unreachable=False,
|
|
132
|
-
job_provisioning_data="",
|
|
133
|
-
offer="",
|
|
134
|
-
backend=BackendType.REMOTE,
|
|
135
|
-
region="",
|
|
136
|
-
price=0,
|
|
137
|
-
)
|
|
138
|
-
session.add(im)
|
|
139
|
-
await session.commit()
|
|
140
|
-
|
|
141
|
-
name = await services_pools.generate_instance_name(
|
|
142
|
-
session=session, project=project, pool_name="test_pool"
|
|
143
|
-
)
|
|
144
|
-
car, _, cdr = name.partition("-")
|
|
145
|
-
assert len(car) > 0
|
|
146
|
-
assert len(cdr) > 0
|
|
147
|
-
|
|
148
|
-
|
|
149
112
|
class TestInstanceModelToInstance:
|
|
150
113
|
@pytest.mark.asyncio
|
|
151
114
|
@pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True)
|
|
@@ -181,11 +144,10 @@ class TestInstanceModelToInstance:
|
|
|
181
144
|
status=InstanceStatus.PENDING,
|
|
182
145
|
unreachable=False,
|
|
183
146
|
project=project,
|
|
184
|
-
pool=None,
|
|
185
147
|
job_provisioning_data='{"ssh_proxy":null, "backend":"local","hostname":"hostname_test","region":"eu-west","price":1.0,"username":"user1","ssh_port":12345,"dockerized":false,"instance_id":"test_instance","instance_type": {"name": "instance", "resources": {"cpus": 1, "memory_mib": 512, "gpus": [], "spot": false, "disk": {"size_mib": 102400}, "description":""}}}',
|
|
186
148
|
offer='{"price":"LOCAL", "price":1.0, "backend":"local", "region":"eu-west-1", "availability":"available","instance": {"name": "instance", "resources": {"cpus": 1, "memory_mib": 512, "gpus": [], "spot": false, "disk": {"size_mib": 102400}, "description":""}}}',
|
|
187
149
|
total_blocks=1,
|
|
188
150
|
busy_blocks=0,
|
|
189
151
|
)
|
|
190
|
-
instance =
|
|
152
|
+
instance = instances_services.instance_model_to_instance(im)
|
|
191
153
|
assert instance == expected_instance
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
from datetime import datetime, timedelta, timezone
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
|
5
|
+
|
|
6
|
+
from dstack._internal.core.models.metrics import Metric
|
|
7
|
+
from dstack._internal.server.services.metrics import get_job_metrics
|
|
8
|
+
from dstack._internal.server.testing.common import (
|
|
9
|
+
create_job,
|
|
10
|
+
create_job_metrics_point,
|
|
11
|
+
create_project,
|
|
12
|
+
create_repo,
|
|
13
|
+
create_run,
|
|
14
|
+
create_user,
|
|
15
|
+
get_job_provisioning_data,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@pytest.mark.asyncio
|
|
20
|
+
@pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True)
|
|
21
|
+
@pytest.mark.usefixtures("test_db", "image_config_mock")
|
|
22
|
+
class TestGetMetrics:
|
|
23
|
+
latest_ts = datetime(2023, 1, 2, 3, 4, 25, tzinfo=timezone.utc)
|
|
24
|
+
ts: tuple[datetime, ...] = (
|
|
25
|
+
latest_ts, # 0
|
|
26
|
+
latest_ts - timedelta(seconds=10), # 1
|
|
27
|
+
latest_ts - timedelta(seconds=20), # 2
|
|
28
|
+
latest_ts - timedelta(seconds=30), # 3
|
|
29
|
+
latest_ts - timedelta(seconds=40), # 4
|
|
30
|
+
latest_ts - timedelta(seconds=50), # 5
|
|
31
|
+
)
|
|
32
|
+
# dt, cpu_usage_sec, memory_usage_bytes, memory_ws_bytes, gpu0_memory_usage_bytes, gpu0_util,
|
|
33
|
+
# gpu1_memory_usage_bytess, gpu1_util
|
|
34
|
+
points: tuple[tuple[datetime, int, int, int, int, int, int, int], ...] = (
|
|
35
|
+
(ts[0], 110, 512, 128, 768, 15, 128, 20),
|
|
36
|
+
(ts[1], 104, 1024, 512, 1024, 10, 256, 10),
|
|
37
|
+
(ts[2], 100, 1024, 512, 1024, 20, 128, 5),
|
|
38
|
+
(ts[3], 90, 512, 512, 2048, 40, 512, 20),
|
|
39
|
+
(ts[4], 90, 1024, 1024, 1024, 0, 128, 0),
|
|
40
|
+
(ts[5], 80, 512, 512, 1024, 10, 256, 0),
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
@pytest.mark.parametrize(
|
|
44
|
+
["params", "ts", "cpu", "mem", "mem_ws", "gpu0_mem", "gpu0_util", "gpu1_mem", "gpu1_util"],
|
|
45
|
+
[
|
|
46
|
+
pytest.param(
|
|
47
|
+
{"limit": 1},
|
|
48
|
+
[ts[0]],
|
|
49
|
+
[60],
|
|
50
|
+
[512],
|
|
51
|
+
[128],
|
|
52
|
+
[768],
|
|
53
|
+
[15],
|
|
54
|
+
[128],
|
|
55
|
+
[20],
|
|
56
|
+
id="limit-1-latest",
|
|
57
|
+
),
|
|
58
|
+
pytest.param(
|
|
59
|
+
{"limit": 3},
|
|
60
|
+
[ts[0], ts[1], ts[2]],
|
|
61
|
+
[60, 40, 100],
|
|
62
|
+
[512, 1024, 1024],
|
|
63
|
+
[128, 512, 512],
|
|
64
|
+
[768, 1024, 1024],
|
|
65
|
+
[15, 10, 20],
|
|
66
|
+
[128, 256, 128],
|
|
67
|
+
[20, 10, 5],
|
|
68
|
+
id="limit-3-latest",
|
|
69
|
+
),
|
|
70
|
+
pytest.param(
|
|
71
|
+
{},
|
|
72
|
+
[ts[0], ts[1], ts[2], ts[3], ts[4]],
|
|
73
|
+
[60, 40, 100, 0, 100],
|
|
74
|
+
[512, 1024, 1024, 512, 1024],
|
|
75
|
+
[128, 512, 512, 512, 1024],
|
|
76
|
+
[768, 1024, 1024, 2048, 1024],
|
|
77
|
+
[15, 10, 20, 40, 0],
|
|
78
|
+
[128, 256, 128, 512, 128],
|
|
79
|
+
[20, 10, 5, 20, 0],
|
|
80
|
+
id="all",
|
|
81
|
+
),
|
|
82
|
+
pytest.param(
|
|
83
|
+
{"after": ts[3]},
|
|
84
|
+
[ts[0], ts[1], ts[2]],
|
|
85
|
+
[60, 40, 100],
|
|
86
|
+
[512, 1024, 1024],
|
|
87
|
+
[128, 512, 512],
|
|
88
|
+
[768, 1024, 1024],
|
|
89
|
+
[15, 10, 20],
|
|
90
|
+
[128, 256, 128],
|
|
91
|
+
[20, 10, 5],
|
|
92
|
+
id="all-after",
|
|
93
|
+
),
|
|
94
|
+
pytest.param(
|
|
95
|
+
{"before": ts[2]},
|
|
96
|
+
[ts[3], ts[4]],
|
|
97
|
+
[0, 100],
|
|
98
|
+
[512, 1024],
|
|
99
|
+
[512, 1024],
|
|
100
|
+
[2048, 1024],
|
|
101
|
+
[40, 0],
|
|
102
|
+
[512, 128],
|
|
103
|
+
[20, 0],
|
|
104
|
+
id="all-before",
|
|
105
|
+
),
|
|
106
|
+
],
|
|
107
|
+
)
|
|
108
|
+
async def test_get_metrics(
|
|
109
|
+
self,
|
|
110
|
+
session: AsyncSession,
|
|
111
|
+
params: dict,
|
|
112
|
+
ts: list[datetime],
|
|
113
|
+
cpu: list[int],
|
|
114
|
+
mem: list[int],
|
|
115
|
+
mem_ws: list[int],
|
|
116
|
+
gpu0_mem: list[int],
|
|
117
|
+
gpu0_util: list[int],
|
|
118
|
+
gpu1_mem: list[int],
|
|
119
|
+
gpu1_util: list[int],
|
|
120
|
+
):
|
|
121
|
+
user = await create_user(session=session)
|
|
122
|
+
project = await create_project(session=session, owner=user)
|
|
123
|
+
repo = await create_repo(
|
|
124
|
+
session=session,
|
|
125
|
+
project_id=project.id,
|
|
126
|
+
)
|
|
127
|
+
run = await create_run(
|
|
128
|
+
session=session,
|
|
129
|
+
project=project,
|
|
130
|
+
repo=repo,
|
|
131
|
+
user=user,
|
|
132
|
+
)
|
|
133
|
+
jpd = get_job_provisioning_data(
|
|
134
|
+
cpu_count=64, memory_gib=128, gpu_count=2, gpu_memory_gib=32
|
|
135
|
+
)
|
|
136
|
+
job = await create_job(
|
|
137
|
+
session=session,
|
|
138
|
+
run=run,
|
|
139
|
+
job_provisioning_data=jpd,
|
|
140
|
+
)
|
|
141
|
+
for dt, _cpu, _mem, _mem_ws, _gpu0_mem, _gpu0_util, _gpu1_mem, _gpu1_util in self.points:
|
|
142
|
+
await create_job_metrics_point(
|
|
143
|
+
session=session,
|
|
144
|
+
job_model=job,
|
|
145
|
+
timestamp=dt,
|
|
146
|
+
cpu_usage_micro=_cpu * 1_000_000,
|
|
147
|
+
memory_usage_bytes=_mem,
|
|
148
|
+
memory_working_set_bytes=_mem_ws,
|
|
149
|
+
gpus_memory_usage_bytes=[_gpu0_mem, _gpu1_mem],
|
|
150
|
+
gpus_util_percent=[_gpu0_util, _gpu1_util],
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
metrics = await get_job_metrics(session, job, **params)
|
|
154
|
+
|
|
155
|
+
assert metrics.metrics == [
|
|
156
|
+
Metric(name="cpu_usage_percent", timestamps=ts, values=cpu),
|
|
157
|
+
Metric(name="memory_usage_bytes", timestamps=ts, values=mem),
|
|
158
|
+
Metric(name="memory_working_set_bytes", timestamps=ts, values=mem_ws),
|
|
159
|
+
Metric(name="cpus_detected_num", timestamps=ts, values=[64] * len(ts)),
|
|
160
|
+
Metric(name="memory_total_bytes", timestamps=ts, values=[137438953472] * len(ts)),
|
|
161
|
+
Metric(name="gpus_detected_num", timestamps=ts, values=[2] * len(ts)),
|
|
162
|
+
Metric(name="gpu_memory_total_bytes", timestamps=ts, values=[34359738368] * len(ts)),
|
|
163
|
+
Metric(name="gpu_memory_usage_bytes_gpu0", timestamps=ts, values=gpu0_mem),
|
|
164
|
+
Metric(name="gpu_memory_usage_bytes_gpu1", timestamps=ts, values=gpu1_mem),
|
|
165
|
+
Metric(name="gpu_util_percent_gpu0", timestamps=ts, values=gpu0_util),
|
|
166
|
+
Metric(name="gpu_util_percent_gpu1", timestamps=ts, values=gpu1_util),
|
|
167
|
+
]
|
|
@@ -7,7 +7,7 @@ from sqlalchemy import select
|
|
|
7
7
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
8
8
|
|
|
9
9
|
from dstack._internal.core.models.repos import RemoteRepoCreds, RemoteRepoInfo, RepoHeadWithCreds
|
|
10
|
-
from dstack._internal.core.models.repos.base import
|
|
10
|
+
from dstack._internal.core.models.repos.base import RepoType
|
|
11
11
|
from dstack._internal.core.models.users import GlobalRole, ProjectRole
|
|
12
12
|
from dstack._internal.server.models import ProjectModel, RepoCredsModel, UserModel
|
|
13
13
|
from dstack._internal.server.services.projects import add_project_member
|
|
@@ -90,7 +90,6 @@ class TestGetRemoteRepo:
|
|
|
90
90
|
):
|
|
91
91
|
repo_info = RemoteRepoInfo(repo_type="remote", repo_name="test")
|
|
92
92
|
legacy_repo_creds = RemoteRepoCreds(
|
|
93
|
-
protocol=RepoProtocol.HTTPS,
|
|
94
93
|
clone_url="https://git.example.com/repo.git",
|
|
95
94
|
private_key=None,
|
|
96
95
|
oauth_token="legacy-oauth-token",
|
|
@@ -104,7 +103,6 @@ class TestGetRemoteRepo:
|
|
|
104
103
|
creds=legacy_repo_creds.dict(),
|
|
105
104
|
)
|
|
106
105
|
user_repo_creds = RemoteRepoCreds(
|
|
107
|
-
protocol=RepoProtocol.HTTPS,
|
|
108
106
|
clone_url="https://git.example.com/repo.git",
|
|
109
107
|
private_key=None,
|
|
110
108
|
oauth_token="user-oauth-token",
|
|
@@ -142,7 +140,6 @@ class TestGetRemoteRepo:
|
|
|
142
140
|
# another user's creds should be ignored
|
|
143
141
|
another_user = await _create_user(session, project, name="another-user")
|
|
144
142
|
another_user_repo_creds = RemoteRepoCreds(
|
|
145
|
-
protocol=RepoProtocol.HTTPS,
|
|
146
143
|
clone_url="https://git.example.com/repo.git",
|
|
147
144
|
private_key=None,
|
|
148
145
|
oauth_token="another-oauth-token",
|
|
@@ -185,7 +182,6 @@ class TestGetRemoteRepo:
|
|
|
185
182
|
repo_info = RemoteRepoInfo(repo_type="remote", repo_name="test")
|
|
186
183
|
if with_legacy_creds:
|
|
187
184
|
legacy_repo_creds = RemoteRepoCreds(
|
|
188
|
-
protocol=RepoProtocol.HTTPS,
|
|
189
185
|
clone_url="https://git.example.com/repo.git",
|
|
190
186
|
private_key=None,
|
|
191
187
|
oauth_token="legacy-oauth-token",
|
|
@@ -201,7 +197,6 @@ class TestGetRemoteRepo:
|
|
|
201
197
|
creds=legacy_repo_creds.dict() if legacy_repo_creds else None,
|
|
202
198
|
)
|
|
203
199
|
user_repo_creds = RemoteRepoCreds(
|
|
204
|
-
protocol=RepoProtocol.HTTPS,
|
|
205
200
|
clone_url="https://git.example.com/repo.git",
|
|
206
201
|
private_key=None,
|
|
207
202
|
oauth_token="user-oauth-token",
|
|
@@ -228,7 +223,6 @@ class TestGetRemoteRepo:
|
|
|
228
223
|
):
|
|
229
224
|
repo_info = RemoteRepoInfo(repo_type="remote", repo_name="test")
|
|
230
225
|
legacy_repo_creds = RemoteRepoCreds(
|
|
231
|
-
protocol=RepoProtocol.HTTPS,
|
|
232
226
|
clone_url="https://git.example.com/repo.git",
|
|
233
227
|
private_key=None,
|
|
234
228
|
oauth_token="legacy-oauth-token",
|
|
@@ -259,7 +253,6 @@ class TestInitRemoteRepo:
|
|
|
259
253
|
):
|
|
260
254
|
repo_info = RemoteRepoInfo(repo_type="remote", repo_name="test")
|
|
261
255
|
repo_creds = RemoteRepoCreds(
|
|
262
|
-
protocol=RepoProtocol.HTTPS,
|
|
263
256
|
clone_url="https://git.example.com/repo.git",
|
|
264
257
|
private_key=None,
|
|
265
258
|
oauth_token="oauth-token",
|
|
@@ -283,7 +276,6 @@ class TestInitRemoteRepo:
|
|
|
283
276
|
old_repo_info = RemoteRepoInfo(repo_type="remote", repo_name="old-name")
|
|
284
277
|
new_repo_info = RemoteRepoInfo(repo_type="remote", repo_name="new-name")
|
|
285
278
|
our_repo_creds = RemoteRepoCreds(
|
|
286
|
-
protocol=RepoProtocol.HTTPS,
|
|
287
279
|
clone_url="https://git.example.com/repo.git",
|
|
288
280
|
private_key=None,
|
|
289
281
|
oauth_token="our-oauth-token",
|
|
@@ -323,7 +315,6 @@ class TestInitRemoteRepo:
|
|
|
323
315
|
creds=None,
|
|
324
316
|
)
|
|
325
317
|
old_repo_creds = RemoteRepoCreds(
|
|
326
|
-
protocol=RepoProtocol.HTTPS,
|
|
327
318
|
clone_url="https://git.example.com/repo.git",
|
|
328
319
|
private_key=None,
|
|
329
320
|
oauth_token="oauth-token",
|
|
@@ -335,7 +326,6 @@ class TestInitRemoteRepo:
|
|
|
335
326
|
creds=old_repo_creds.dict(),
|
|
336
327
|
)
|
|
337
328
|
new_repo_creds = RemoteRepoCreds(
|
|
338
|
-
protocol=RepoProtocol.HTTPS,
|
|
339
329
|
clone_url="ssh://git@git.example.com/repo.git",
|
|
340
330
|
private_key="private-key",
|
|
341
331
|
oauth_token=None,
|
|
@@ -357,7 +347,6 @@ class TestInitRemoteRepo:
|
|
|
357
347
|
):
|
|
358
348
|
repo_info = RemoteRepoInfo(repo_type="remote", repo_name="test")
|
|
359
349
|
legacy_repo_creds = RemoteRepoCreds(
|
|
360
|
-
protocol=RepoProtocol.HTTPS,
|
|
361
350
|
clone_url="https://git.example.com/repo.git",
|
|
362
351
|
private_key=None,
|
|
363
352
|
oauth_token="legacy-oauth-token",
|
|
@@ -371,7 +360,6 @@ class TestInitRemoteRepo:
|
|
|
371
360
|
creds=legacy_repo_creds.dict(),
|
|
372
361
|
)
|
|
373
362
|
our_repo_creds = RemoteRepoCreds(
|
|
374
|
-
protocol=RepoProtocol.HTTPS,
|
|
375
363
|
clone_url="https://git.example.com/repo.git",
|
|
376
364
|
private_key=None,
|
|
377
365
|
oauth_token="our-oauth-token",
|
|
@@ -384,7 +372,6 @@ class TestInitRemoteRepo:
|
|
|
384
372
|
)
|
|
385
373
|
another_user = await _create_user(session, project, name="another-user")
|
|
386
374
|
another_user_repo_creds = RemoteRepoCreds(
|
|
387
|
-
protocol=RepoProtocol.HTTPS,
|
|
388
375
|
clone_url="https://git.example.com/repo.git",
|
|
389
376
|
private_key=None,
|
|
390
377
|
oauth_token="another-oauth-token",
|
|
@@ -15,7 +15,6 @@ from dstack._internal.server.services.jobs import check_can_attach_job_volumes
|
|
|
15
15
|
from dstack._internal.server.services.runs import scale_run_replicas
|
|
16
16
|
from dstack._internal.server.testing.common import (
|
|
17
17
|
create_job,
|
|
18
|
-
create_pool,
|
|
19
18
|
create_project,
|
|
20
19
|
create_repo,
|
|
21
20
|
create_run,
|
|
@@ -39,9 +38,6 @@ async def make_run(
|
|
|
39
38
|
session=session,
|
|
40
39
|
project_id=project.id,
|
|
41
40
|
)
|
|
42
|
-
project.default_pool = await create_pool(
|
|
43
|
-
session=session, project=project, pool_name="default-pool"
|
|
44
|
-
)
|
|
45
41
|
run_name = "test-run"
|
|
46
42
|
profile = Profile(name="test-profile")
|
|
47
43
|
run_spec = get_run_spec(
|