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
dstack/api/_public/runs.py
CHANGED
|
@@ -16,23 +16,21 @@ import dstack.api as api
|
|
|
16
16
|
from dstack._internal.core.consts import DSTACK_RUNNER_HTTP_PORT, DSTACK_RUNNER_SSH_PORT
|
|
17
17
|
from dstack._internal.core.errors import ClientError, ConfigurationError, ResourceNotExistsError
|
|
18
18
|
from dstack._internal.core.models.backends.base import BackendType
|
|
19
|
-
from dstack._internal.core.models.common import ApplyAction
|
|
20
19
|
from dstack._internal.core.models.configurations import AnyRunConfiguration, PortMapping
|
|
21
|
-
from dstack._internal.core.models.pools import Instance
|
|
22
20
|
from dstack._internal.core.models.profiles import (
|
|
23
21
|
CreationPolicy,
|
|
24
22
|
Profile,
|
|
25
23
|
ProfileRetryPolicy,
|
|
26
24
|
SpotPolicy,
|
|
27
25
|
TerminationPolicy,
|
|
26
|
+
UtilizationPolicy,
|
|
28
27
|
)
|
|
29
28
|
from dstack._internal.core.models.repos.base import Repo
|
|
29
|
+
from dstack._internal.core.models.repos.virtual import VirtualRepo
|
|
30
30
|
from dstack._internal.core.models.resources import ResourcesSpec
|
|
31
31
|
from dstack._internal.core.models.runs import (
|
|
32
32
|
Job,
|
|
33
33
|
JobSpec,
|
|
34
|
-
PoolInstanceOffers,
|
|
35
|
-
Requirements,
|
|
36
34
|
RunPlan,
|
|
37
35
|
RunSpec,
|
|
38
36
|
RunStatus,
|
|
@@ -190,14 +188,14 @@ class Run(ABC):
|
|
|
190
188
|
job_num: int = 0,
|
|
191
189
|
) -> Iterable[bytes]:
|
|
192
190
|
"""
|
|
193
|
-
Iterate through run's log messages
|
|
191
|
+
Iterate through run's log messages.
|
|
194
192
|
|
|
195
193
|
Args:
|
|
196
|
-
start_time:
|
|
197
|
-
diagnose:
|
|
194
|
+
start_time: Minimal log timestamp.
|
|
195
|
+
diagnose: Return runner logs if `True`.
|
|
198
196
|
|
|
199
197
|
Yields:
|
|
200
|
-
|
|
198
|
+
Log messages.
|
|
201
199
|
"""
|
|
202
200
|
if diagnose is False and self._ssh_attach is not None:
|
|
203
201
|
yield from self._attached_logs()
|
|
@@ -227,17 +225,17 @@ class Run(ABC):
|
|
|
227
225
|
|
|
228
226
|
def refresh(self):
|
|
229
227
|
"""
|
|
230
|
-
Get up-to-date run info
|
|
228
|
+
Get up-to-date run info.
|
|
231
229
|
"""
|
|
232
230
|
self._run = self._api_client.runs.get(self._project, self._run.run_spec.run_name)
|
|
233
231
|
logger.debug("Refreshed run %s: %s", self.name, self.status)
|
|
234
232
|
|
|
235
233
|
def stop(self, abort: bool = False):
|
|
236
234
|
"""
|
|
237
|
-
Terminate the instance and detach
|
|
235
|
+
Terminate the instance and detach.
|
|
238
236
|
|
|
239
237
|
Args:
|
|
240
|
-
abort:
|
|
238
|
+
abort: Gracefully stop the run if `False`.
|
|
241
239
|
"""
|
|
242
240
|
self._api_client.runs.stop(self._project, [self.name], abort)
|
|
243
241
|
logger.debug("%s run %s", "Aborted" if abort else "Stopped", self.name)
|
|
@@ -255,7 +253,7 @@ class Run(ABC):
|
|
|
255
253
|
Establish an SSH tunnel to the instance and update SSH config
|
|
256
254
|
|
|
257
255
|
Args:
|
|
258
|
-
ssh_identity_file: SSH keypair to access instances
|
|
256
|
+
ssh_identity_file: SSH keypair to access instances.
|
|
259
257
|
|
|
260
258
|
Raises:
|
|
261
259
|
dstack.api.PortUsedError: If ports are in use or the run is attached by another process.
|
|
@@ -392,7 +390,7 @@ class ServiceModel:
|
|
|
392
390
|
|
|
393
391
|
class RunCollection:
|
|
394
392
|
"""
|
|
395
|
-
Operations with runs
|
|
393
|
+
Operations with runs.
|
|
396
394
|
"""
|
|
397
395
|
|
|
398
396
|
def __init__(
|
|
@@ -405,6 +403,122 @@ class RunCollection:
|
|
|
405
403
|
self._project = project
|
|
406
404
|
self._client = client
|
|
407
405
|
|
|
406
|
+
def get_run_plan(
|
|
407
|
+
self,
|
|
408
|
+
configuration: AnyRunConfiguration,
|
|
409
|
+
repo: Optional[Repo] = None,
|
|
410
|
+
profile: Optional[Profile] = None,
|
|
411
|
+
configuration_path: Optional[str] = None,
|
|
412
|
+
) -> RunPlan:
|
|
413
|
+
"""
|
|
414
|
+
Get a run plan.
|
|
415
|
+
Use this method to see the run plan before applying the cofiguration.
|
|
416
|
+
|
|
417
|
+
Args:
|
|
418
|
+
configuration (Union[Task, Service, DevEnvironment]): The run configuration.
|
|
419
|
+
repo (Union[LocalRepo, RemoteRepo, VirtualRepo, None]):
|
|
420
|
+
The repo to use for the run. Pass `None` if repo is not needed.
|
|
421
|
+
profile: The profile to use for the run.
|
|
422
|
+
configuration_path: The path to the configuration file. Omit if the configuration is not loaded from a file.
|
|
423
|
+
|
|
424
|
+
Returns:
|
|
425
|
+
Run plan.
|
|
426
|
+
"""
|
|
427
|
+
if repo is None:
|
|
428
|
+
repo = VirtualRepo()
|
|
429
|
+
|
|
430
|
+
run_spec = RunSpec(
|
|
431
|
+
run_name=configuration.name,
|
|
432
|
+
repo_id=repo.repo_id,
|
|
433
|
+
repo_data=repo.run_repo_data,
|
|
434
|
+
repo_code_hash=None, # `apply_plan` will fill it
|
|
435
|
+
working_dir=configuration.working_dir,
|
|
436
|
+
configuration_path=configuration_path,
|
|
437
|
+
configuration=configuration,
|
|
438
|
+
profile=profile,
|
|
439
|
+
ssh_key_pub=Path(self._client.ssh_identity_file + ".pub").read_text().strip(),
|
|
440
|
+
)
|
|
441
|
+
logger.debug("Getting run plan")
|
|
442
|
+
run_plan = self._api_client.runs.get_plan(self._project, run_spec)
|
|
443
|
+
return run_plan
|
|
444
|
+
|
|
445
|
+
def apply_plan(
|
|
446
|
+
self,
|
|
447
|
+
run_plan: RunPlan,
|
|
448
|
+
repo: Optional[Repo] = None,
|
|
449
|
+
reserve_ports: bool = True,
|
|
450
|
+
) -> Run:
|
|
451
|
+
"""
|
|
452
|
+
Apply the run plan.
|
|
453
|
+
Use this method to apply run plans returned by `get_run_plan`.
|
|
454
|
+
|
|
455
|
+
Args:
|
|
456
|
+
run_plan: The result of `get_run_plan` call.
|
|
457
|
+
repo (Union[LocalRepo, RemoteRepo, VirtualRepo, None]):
|
|
458
|
+
The repo to use for the run. Should be the same repo that is passed to `get_run_plan`.
|
|
459
|
+
reserve_ports: Reserve local ports before applying. Use if you'll attach to the run.
|
|
460
|
+
|
|
461
|
+
Returns:
|
|
462
|
+
Submitted run.
|
|
463
|
+
"""
|
|
464
|
+
ports_lock = None
|
|
465
|
+
if reserve_ports:
|
|
466
|
+
# TODO handle multiple jobs
|
|
467
|
+
ports_lock = _reserve_ports(run_plan.job_plans[0].job_spec)
|
|
468
|
+
|
|
469
|
+
if repo is None:
|
|
470
|
+
repo = VirtualRepo()
|
|
471
|
+
else:
|
|
472
|
+
# Do not upload the diff without a repo (a default virtual repo)
|
|
473
|
+
# since upload_code() requires a repo to be initialized.
|
|
474
|
+
with tempfile.TemporaryFile("w+b") as fp:
|
|
475
|
+
run_plan.run_spec.repo_code_hash = repo.write_code_file(fp)
|
|
476
|
+
fp.seek(0)
|
|
477
|
+
self._api_client.repos.upload_code(
|
|
478
|
+
project_name=self._project,
|
|
479
|
+
repo_id=repo.repo_id,
|
|
480
|
+
code_hash=run_plan.run_spec.repo_code_hash,
|
|
481
|
+
fp=fp,
|
|
482
|
+
)
|
|
483
|
+
run = self._api_client.runs.apply_plan(self._project, run_plan)
|
|
484
|
+
return self._model_to_submitted_run(run, ports_lock)
|
|
485
|
+
|
|
486
|
+
def apply_configuration(
|
|
487
|
+
self,
|
|
488
|
+
configuration: AnyRunConfiguration,
|
|
489
|
+
repo: Optional[Repo] = None,
|
|
490
|
+
profile: Optional[Profile] = None,
|
|
491
|
+
configuration_path: Optional[str] = None,
|
|
492
|
+
reserve_ports: bool = True,
|
|
493
|
+
) -> Run:
|
|
494
|
+
"""
|
|
495
|
+
Apply the run configuration.
|
|
496
|
+
Use this method to apply configurations without getting a run plan first.
|
|
497
|
+
|
|
498
|
+
Args:
|
|
499
|
+
configuration (Union[Task, Service, DevEnvironment]): The run configuration.
|
|
500
|
+
repo (Union[LocalRepo, RemoteRepo, VirtualRepo, None]):
|
|
501
|
+
The repo to use for the run. Pass `None` if repo is not needed.
|
|
502
|
+
profile: The profile to use for the run.
|
|
503
|
+
configuration_path: The path to the configuration file. Omit if the configuration is not loaded from a file.
|
|
504
|
+
reserve_ports: Reserve local ports before applying. Use if you'll attach to the run.
|
|
505
|
+
|
|
506
|
+
Returns:
|
|
507
|
+
Submitted run.
|
|
508
|
+
"""
|
|
509
|
+
run_plan = self.get_run_plan(
|
|
510
|
+
configuration=configuration,
|
|
511
|
+
repo=repo,
|
|
512
|
+
profile=profile,
|
|
513
|
+
configuration_path=configuration_path,
|
|
514
|
+
)
|
|
515
|
+
run = self.apply_plan(
|
|
516
|
+
run_plan=run_plan,
|
|
517
|
+
repo=repo,
|
|
518
|
+
reserve_ports=reserve_ports,
|
|
519
|
+
)
|
|
520
|
+
return run
|
|
521
|
+
|
|
408
522
|
def submit(
|
|
409
523
|
self,
|
|
410
524
|
configuration: AnyRunConfiguration,
|
|
@@ -422,31 +536,30 @@ class RunCollection:
|
|
|
422
536
|
run_name: Optional[str] = None,
|
|
423
537
|
reserve_ports: bool = True,
|
|
424
538
|
) -> Run:
|
|
425
|
-
"""
|
|
426
|
-
Submit a run
|
|
539
|
+
# """
|
|
540
|
+
# Submit a run
|
|
427
541
|
|
|
428
|
-
Args:
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
542
|
+
# Args:
|
|
543
|
+
# configuration (Union[Task, Service]): A run configuration.
|
|
544
|
+
# configuration_path: The path to the configuration file, relative to the root directory of the repo.
|
|
545
|
+
# repo (Union[LocalRepo, RemoteRepo, VirtualRepo]): A repo to mount to the run.
|
|
546
|
+
# backends: A list of allowed backend for provisioning.
|
|
547
|
+
# regions: A list of cloud regions for provisioning.
|
|
548
|
+
# resources: The requirements to run the configuration. Overrides the configuration's resources.
|
|
549
|
+
# spot_policy: A spot policy for provisioning.
|
|
550
|
+
# retry_policy (RetryPolicy): A retry policy.
|
|
551
|
+
# max_duration: The max instance running duration in seconds.
|
|
552
|
+
# max_price: The max instance price in dollars per hour for provisioning.
|
|
553
|
+
# working_dir: A working directory relative to the repo root directory
|
|
554
|
+
# run_name: A desired name of the run. Must be unique in the project. If not specified, a random name is assigned.
|
|
555
|
+
# reserve_ports: Whether local ports should be reserved in advance.
|
|
442
556
|
|
|
443
|
-
Returns:
|
|
444
|
-
|
|
445
|
-
"""
|
|
557
|
+
# Returns:
|
|
558
|
+
# Submitted run.
|
|
559
|
+
# """
|
|
560
|
+
logger.warning("The submit() method is deprecated in favor of apply_configuration().")
|
|
446
561
|
if repo is None:
|
|
447
|
-
repo =
|
|
448
|
-
if repo is None:
|
|
449
|
-
raise ConfigurationError("Repo is required for this type of configuration")
|
|
562
|
+
repo = VirtualRepo()
|
|
450
563
|
# TODO: Add Git credentials to RemoteRepo and if they are set, pass them here to RepoCollection.init
|
|
451
564
|
self._client.repos.init(repo)
|
|
452
565
|
|
|
@@ -467,24 +580,21 @@ class RunCollection:
|
|
|
467
580
|
)
|
|
468
581
|
return self.exec_plan(run_plan, repo, reserve_ports=reserve_ports)
|
|
469
582
|
|
|
470
|
-
|
|
471
|
-
return self._api_client.runs.get_offers(self._project, profile, requirements)
|
|
472
|
-
|
|
473
|
-
def create_instance(self, profile: Profile, requirements: Requirements) -> Instance:
|
|
474
|
-
return self._api_client.runs.create_instance(self._project, profile, requirements)
|
|
475
|
-
|
|
476
|
-
# TODO: [Andrey] I guess we need to drop profile-related fields (currently retry is not reflected there)
|
|
583
|
+
# Deprecated in favor of get_run_plan()
|
|
477
584
|
def get_plan(
|
|
478
585
|
self,
|
|
479
586
|
configuration: AnyRunConfiguration,
|
|
480
587
|
repo: Optional[Repo] = None,
|
|
481
588
|
configuration_path: Optional[str] = None,
|
|
589
|
+
# Unused profile args are deprecated and removed but
|
|
590
|
+
# kept for signature backward compatibility.
|
|
482
591
|
backends: Optional[List[BackendType]] = None,
|
|
483
592
|
regions: Optional[List[str]] = None,
|
|
484
593
|
instance_types: Optional[List[str]] = None,
|
|
485
594
|
resources: Optional[ResourcesSpec] = None,
|
|
486
595
|
spot_policy: Optional[SpotPolicy] = None,
|
|
487
596
|
retry_policy: Optional[ProfileRetryPolicy] = None,
|
|
597
|
+
utilization_policy: Optional[UtilizationPolicy] = None,
|
|
488
598
|
max_duration: Optional[Union[int, str]] = None,
|
|
489
599
|
max_price: Optional[float] = None,
|
|
490
600
|
working_dir: Optional[str] = None,
|
|
@@ -504,11 +614,9 @@ class RunCollection:
|
|
|
504
614
|
# Returns:
|
|
505
615
|
# run plan
|
|
506
616
|
# """
|
|
507
|
-
|
|
617
|
+
logger.warning("The get_plan() method is deprecated in favor of get_run_plan().")
|
|
508
618
|
if repo is None:
|
|
509
|
-
repo =
|
|
510
|
-
if repo is None:
|
|
511
|
-
raise ConfigurationError("Repo is required for this type of configuration")
|
|
619
|
+
repo = VirtualRepo()
|
|
512
620
|
|
|
513
621
|
if working_dir is None:
|
|
514
622
|
working_dir = "."
|
|
@@ -534,16 +642,12 @@ class RunCollection:
|
|
|
534
642
|
reservation=reservation,
|
|
535
643
|
spot_policy=spot_policy,
|
|
536
644
|
retry=None,
|
|
537
|
-
|
|
645
|
+
utilization_policy=utilization_policy,
|
|
538
646
|
max_duration=max_duration,
|
|
539
647
|
stop_duration=stop_duration,
|
|
540
648
|
max_price=max_price,
|
|
541
|
-
pool_name=pool_name,
|
|
542
|
-
instance_name=instance_name,
|
|
543
649
|
creation_policy=creation_policy,
|
|
544
650
|
idle_duration=idle_duration,
|
|
545
|
-
termination_policy=termination_policy,
|
|
546
|
-
termination_idle_time=termination_policy_idle,
|
|
547
651
|
)
|
|
548
652
|
run_spec = RunSpec(
|
|
549
653
|
run_name=run_name,
|
|
@@ -574,45 +678,28 @@ class RunCollection:
|
|
|
574
678
|
reserve_ports: bool = True,
|
|
575
679
|
) -> Run:
|
|
576
680
|
# """
|
|
577
|
-
# Execute run plan
|
|
578
|
-
|
|
681
|
+
# Execute the run plan.
|
|
682
|
+
|
|
579
683
|
# Args:
|
|
580
|
-
# run_plan:
|
|
581
|
-
# repo:
|
|
582
|
-
# reserve_ports:
|
|
583
|
-
|
|
684
|
+
# run_plan: Result of `get_run_plan` call.
|
|
685
|
+
# repo: Repo to use for the run.
|
|
686
|
+
# reserve_ports: Reserve local ports before submit.
|
|
687
|
+
|
|
584
688
|
# Returns:
|
|
585
|
-
#
|
|
689
|
+
# Submitted run.
|
|
586
690
|
# """
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
# TODO handle multiple jobs
|
|
590
|
-
ports_lock = _reserve_ports(run_plan.job_plans[0].job_spec)
|
|
591
|
-
|
|
592
|
-
with tempfile.TemporaryFile("w+b") as fp:
|
|
593
|
-
run_plan.run_spec.repo_code_hash = repo.write_code_file(fp)
|
|
594
|
-
fp.seek(0)
|
|
595
|
-
self._api_client.repos.upload_code(
|
|
596
|
-
self._project, repo.repo_id, run_plan.run_spec.repo_code_hash, fp
|
|
597
|
-
)
|
|
598
|
-
# Calling submit when action is CREATE since apply_plan is not backward-compatible.
|
|
599
|
-
# Otherwise, apply_plan can replace submit, i.e. it creates the run if it does not exist.
|
|
600
|
-
# TODO: Remove in 0.19
|
|
601
|
-
if run_plan.action == ApplyAction.UPDATE:
|
|
602
|
-
run = self._api_client.runs.apply_plan(self._project, run_plan)
|
|
603
|
-
else:
|
|
604
|
-
run = self._api_client.runs.submit(self._project, run_plan.run_spec)
|
|
605
|
-
return self._model_to_submitted_run(run, ports_lock)
|
|
691
|
+
logger.warning("The exec_plan() method is deprecated in favor of apply_plan().")
|
|
692
|
+
return self.apply_plan(run_plan=run_plan, repo=repo, reserve_ports=reserve_ports)
|
|
606
693
|
|
|
607
694
|
def list(self, all: bool = False) -> List[Run]:
|
|
608
695
|
"""
|
|
609
|
-
List runs
|
|
696
|
+
List runs.
|
|
610
697
|
|
|
611
698
|
Args:
|
|
612
|
-
all:
|
|
699
|
+
all: Show all runs (active and finished) if `True`.
|
|
613
700
|
|
|
614
701
|
Returns:
|
|
615
|
-
|
|
702
|
+
List of runs.
|
|
616
703
|
"""
|
|
617
704
|
# Return only one page of latest runs (<=100). Returning all the pages may be costly.
|
|
618
705
|
# TODO: Consider introducing `since` filter with a reasonable default.
|
|
@@ -631,13 +718,13 @@ class RunCollection:
|
|
|
631
718
|
|
|
632
719
|
def get(self, run_name: str) -> Optional[Run]:
|
|
633
720
|
"""
|
|
634
|
-
Get run by run name
|
|
721
|
+
Get run by run name.
|
|
635
722
|
|
|
636
723
|
Args:
|
|
637
|
-
run_name:
|
|
724
|
+
run_name: Run name.
|
|
638
725
|
|
|
639
726
|
Returns:
|
|
640
|
-
The run or `None` if not found
|
|
727
|
+
The run or `None` if not found.
|
|
641
728
|
"""
|
|
642
729
|
try:
|
|
643
730
|
run = self._api_client.runs.get(self._project, run_name)
|
dstack/api/server/__init__.py
CHANGED
|
@@ -13,7 +13,6 @@ from dstack.api.server._fleets import FleetsAPIClient
|
|
|
13
13
|
from dstack.api.server._gateways import GatewaysAPIClient
|
|
14
14
|
from dstack.api.server._logs import LogsAPIClient
|
|
15
15
|
from dstack.api.server._metrics import MetricsAPIClient
|
|
16
|
-
from dstack.api.server._pools import PoolAPIClient
|
|
17
16
|
from dstack.api.server._projects import ProjectsAPIClient
|
|
18
17
|
from dstack.api.server._repos import ReposAPIClient
|
|
19
18
|
from dstack.api.server._runs import RunsAPIClient
|
|
@@ -30,23 +29,26 @@ _RETRY_INTERVAL = 1
|
|
|
30
29
|
|
|
31
30
|
class APIClient:
|
|
32
31
|
"""
|
|
33
|
-
Low-level API client for interacting with dstack server.
|
|
32
|
+
Low-level API client for interacting with the `dstack` server.
|
|
33
|
+
Supports all HTTP API endpoints.
|
|
34
34
|
|
|
35
35
|
Attributes:
|
|
36
36
|
users: operations with users
|
|
37
37
|
projects: operations with projects
|
|
38
38
|
backends: operations with backends
|
|
39
|
+
fleets: operations with fleets
|
|
39
40
|
runs: operations with runs
|
|
41
|
+
metrics: operations with metrics
|
|
40
42
|
logs: operations with logs
|
|
41
43
|
gateways: operations with gateways
|
|
42
|
-
|
|
44
|
+
volumes: operations with volumes
|
|
43
45
|
"""
|
|
44
46
|
|
|
45
47
|
def __init__(self, base_url: str, token: str):
|
|
46
48
|
"""
|
|
47
49
|
Args:
|
|
48
|
-
base_url: API endpoints prefix, e.g. `http://127.0.0.1:3000
|
|
49
|
-
token: API token
|
|
50
|
+
base_url: The API endpoints prefix, e.g. `http://127.0.0.1:3000/`.
|
|
51
|
+
token: The API token.
|
|
50
52
|
"""
|
|
51
53
|
self._base_url = base_url.rstrip("/")
|
|
52
54
|
self._token = token
|
|
@@ -72,6 +74,10 @@ class APIClient:
|
|
|
72
74
|
def backends(self) -> BackendsAPIClient:
|
|
73
75
|
return BackendsAPIClient(self._request)
|
|
74
76
|
|
|
77
|
+
@property
|
|
78
|
+
def fleets(self) -> FleetsAPIClient:
|
|
79
|
+
return FleetsAPIClient(self._request)
|
|
80
|
+
|
|
75
81
|
@property
|
|
76
82
|
def repos(self) -> ReposAPIClient:
|
|
77
83
|
return ReposAPIClient(self._request)
|
|
@@ -96,14 +102,6 @@ class APIClient:
|
|
|
96
102
|
def gateways(self) -> GatewaysAPIClient:
|
|
97
103
|
return GatewaysAPIClient(self._request)
|
|
98
104
|
|
|
99
|
-
@property
|
|
100
|
-
def pool(self) -> PoolAPIClient:
|
|
101
|
-
return PoolAPIClient(self._request)
|
|
102
|
-
|
|
103
|
-
@property
|
|
104
|
-
def fleets(self) -> FleetsAPIClient:
|
|
105
|
-
return FleetsAPIClient(self._request)
|
|
106
|
-
|
|
107
105
|
@property
|
|
108
106
|
def volumes(self) -> VolumesAPIClient:
|
|
109
107
|
return VolumesAPIClient(self._request)
|
dstack/api/server/_backends.py
CHANGED
|
@@ -2,10 +2,8 @@ from typing import List
|
|
|
2
2
|
|
|
3
3
|
from pydantic import parse_obj_as
|
|
4
4
|
|
|
5
|
-
from dstack._internal.core.models
|
|
6
|
-
|
|
7
|
-
AnyConfigInfoWithCredsPartial,
|
|
8
|
-
AnyConfigValues,
|
|
5
|
+
from dstack._internal.core.backends.models import (
|
|
6
|
+
AnyBackendConfigWithCreds,
|
|
9
7
|
)
|
|
10
8
|
from dstack._internal.core.models.backends.base import BackendType
|
|
11
9
|
from dstack._internal.server.schemas.backends import DeleteBackendsRequest
|
|
@@ -17,26 +15,24 @@ class BackendsAPIClient(APIClientGroup):
|
|
|
17
15
|
resp = self._request("/api/backends/list_types")
|
|
18
16
|
return parse_obj_as(List[BackendType], resp.json())
|
|
19
17
|
|
|
20
|
-
def config_values(self, config: AnyConfigInfoWithCredsPartial) -> AnyConfigValues:
|
|
21
|
-
resp = self._request("/api/backends/config_values", body=config.json())
|
|
22
|
-
return parse_obj_as(AnyConfigValues, resp.json())
|
|
23
|
-
|
|
24
18
|
def create(
|
|
25
|
-
self, project_name: str, config:
|
|
26
|
-
) ->
|
|
19
|
+
self, project_name: str, config: AnyBackendConfigWithCreds
|
|
20
|
+
) -> AnyBackendConfigWithCreds:
|
|
27
21
|
resp = self._request(f"/api/project/{project_name}/backends/create", body=config.json())
|
|
28
|
-
return parse_obj_as(
|
|
22
|
+
return parse_obj_as(AnyBackendConfigWithCreds, resp.json())
|
|
29
23
|
|
|
30
24
|
def update(
|
|
31
|
-
self, project_name: str, config:
|
|
32
|
-
) ->
|
|
25
|
+
self, project_name: str, config: AnyBackendConfigWithCreds
|
|
26
|
+
) -> AnyBackendConfigWithCreds:
|
|
33
27
|
resp = self._request(f"/api/project/{project_name}/backends/update", body=config.json())
|
|
34
|
-
return parse_obj_as(
|
|
28
|
+
return parse_obj_as(AnyBackendConfigWithCreds, resp.json())
|
|
35
29
|
|
|
36
30
|
def delete(self, project_name: str, backends_names: List[BackendType]):
|
|
37
31
|
body = DeleteBackendsRequest(backends_names=backends_names)
|
|
38
32
|
self._request(f"/api/project/{project_name}/backends/delete", body=body.json())
|
|
39
33
|
|
|
40
|
-
def config_info(
|
|
34
|
+
def config_info(
|
|
35
|
+
self, project_name: str, backend_name: BackendType
|
|
36
|
+
) -> AnyBackendConfigWithCreds:
|
|
41
37
|
resp = self._request(f"/api/project/{project_name}/backends/{backend_name}/config_info")
|
|
42
|
-
return parse_obj_as(
|
|
38
|
+
return parse_obj_as(AnyBackendConfigWithCreds, resp.json())
|
dstack/api/server/_fleets.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import List, Optional
|
|
1
|
+
from typing import Any, Dict, List, Optional
|
|
2
2
|
|
|
3
3
|
from pydantic import parse_obj_as
|
|
4
4
|
|
|
@@ -22,7 +22,7 @@ class FleetsAPIClient(APIClientGroup):
|
|
|
22
22
|
body = GetFleetRequest(name=name)
|
|
23
23
|
resp = self._request(
|
|
24
24
|
f"/api/project/{project_name}/fleets/get",
|
|
25
|
-
body=body.json(
|
|
25
|
+
body=body.json(),
|
|
26
26
|
)
|
|
27
27
|
return parse_obj_as(Fleet.__response__, resp.json())
|
|
28
28
|
|
|
@@ -55,60 +55,20 @@ class FleetsAPIClient(APIClientGroup):
|
|
|
55
55
|
self._request(f"/api/project/{project_name}/fleets/delete_instances", body=body.json())
|
|
56
56
|
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
58
|
+
def _get_fleet_spec_excludes(fleet_spec: FleetSpec) -> Optional[Dict]:
|
|
59
|
+
"""
|
|
60
|
+
Returns `fleet_spec` exclude mapping to exclude certain fields from the request.
|
|
61
|
+
Use this method to exclude new fields when they are not set to keep
|
|
62
|
+
clients backward-compatibility with older servers.
|
|
63
|
+
"""
|
|
64
|
+
spec_excludes: Dict[str, Any] = {}
|
|
65
|
+
configuration_excludes: Dict[str, Any] = {}
|
|
64
66
|
profile_excludes: set[str] = set()
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
#
|
|
69
|
-
|
|
70
|
-
spec_excludes["configuration_path"] = True
|
|
71
|
-
if fleet_spec.configuration.ssh_config is not None:
|
|
72
|
-
if fleet_spec.configuration.ssh_config.proxy_jump is None:
|
|
73
|
-
ssh_config_excludes["proxy_jump"] = True
|
|
74
|
-
if all(
|
|
75
|
-
isinstance(h, str) or h.proxy_jump is None
|
|
76
|
-
for h in fleet_spec.configuration.ssh_config.hosts
|
|
77
|
-
):
|
|
78
|
-
ssh_hosts_excludes.add("proxy_jump")
|
|
79
|
-
if all(
|
|
80
|
-
isinstance(h, str) or h.internal_ip is None
|
|
81
|
-
for h in fleet_spec.configuration.ssh_config.hosts
|
|
82
|
-
):
|
|
83
|
-
ssh_hosts_excludes.add("internal_ip")
|
|
84
|
-
if all(
|
|
85
|
-
isinstance(h, str) or h.blocks == 1 for h in fleet_spec.configuration.ssh_config.hosts
|
|
86
|
-
):
|
|
87
|
-
ssh_hosts_excludes.add("blocks")
|
|
88
|
-
# client >= 0.18.30 / server <= 0.18.29 compatibility tweak
|
|
89
|
-
if fleet_spec.configuration.reservation is None:
|
|
90
|
-
configuration_excludes["reservation"] = True
|
|
91
|
-
if fleet_spec.profile is not None and fleet_spec.profile.reservation is None:
|
|
92
|
-
profile_excludes.add("reservation")
|
|
93
|
-
if fleet_spec.configuration.idle_duration is None:
|
|
94
|
-
configuration_excludes["idle_duration"] = True
|
|
95
|
-
if fleet_spec.profile is not None and fleet_spec.profile.idle_duration is None:
|
|
96
|
-
profile_excludes.add("idle_duration")
|
|
97
|
-
# client >= 0.18.38 / server <= 0.18.37 compatibility tweak
|
|
98
|
-
if fleet_spec.profile is not None and fleet_spec.profile.stop_duration is None:
|
|
99
|
-
profile_excludes.add("stop_duration")
|
|
100
|
-
# client >= 0.18.41 / server <= 0.18.40 compatibility tweak
|
|
101
|
-
if fleet_spec.configuration.availability_zones is None:
|
|
102
|
-
configuration_excludes["availability_zones"] = True
|
|
103
|
-
if fleet_spec.profile is not None and fleet_spec.profile.availability_zones is None:
|
|
104
|
-
profile_excludes.add("availability_zones")
|
|
105
|
-
if fleet_spec.configuration.blocks == 1:
|
|
106
|
-
configuration_excludes["blocks"] = True
|
|
107
|
-
|
|
108
|
-
if ssh_hosts_excludes:
|
|
109
|
-
ssh_config_excludes["hosts"] = {"__all__": ssh_hosts_excludes}
|
|
110
|
-
if ssh_config_excludes:
|
|
111
|
-
configuration_excludes["ssh_config"] = ssh_config_excludes
|
|
67
|
+
# Fields can be excluded like this:
|
|
68
|
+
# if fleet_spec.configuration.availability_zones is None:
|
|
69
|
+
# configuration_excludes["availability_zones"] = True
|
|
70
|
+
# if fleet_spec.profile is not None and fleet_spec.profile.availability_zones is None:
|
|
71
|
+
# profile_excludes.add("availability_zones")
|
|
112
72
|
if configuration_excludes:
|
|
113
73
|
spec_excludes["configuration"] = configuration_excludes
|
|
114
74
|
if profile_excludes:
|
dstack/api/server/_gateways.py
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
from typing import List
|
|
1
|
+
from typing import List
|
|
2
2
|
|
|
3
3
|
from pydantic import parse_obj_as
|
|
4
4
|
|
|
5
|
-
from dstack._internal.core.models.backends.base import BackendType
|
|
6
5
|
from dstack._internal.core.models.gateways import Gateway, GatewayConfiguration
|
|
7
6
|
from dstack._internal.server.schemas.gateways import (
|
|
8
7
|
CreateGatewayRequest,
|
|
@@ -24,22 +23,12 @@ class GatewaysAPIClient(APIClientGroup):
|
|
|
24
23
|
resp = self._request(f"/api/project/{project_name}/gateways/get", body=body.json())
|
|
25
24
|
return parse_obj_as(Gateway.__response__, resp.json())
|
|
26
25
|
|
|
27
|
-
# gateway_name, backend_type, region are left for backward-compatibility with 0.18.x
|
|
28
|
-
# TODO: Remove in 0.19
|
|
29
26
|
def create(
|
|
30
27
|
self,
|
|
31
28
|
project_name: str,
|
|
32
|
-
|
|
33
|
-
backend_type: Optional[BackendType] = None,
|
|
34
|
-
region: Optional[str] = None,
|
|
35
|
-
configuration: Optional[GatewayConfiguration] = None,
|
|
29
|
+
configuration: GatewayConfiguration,
|
|
36
30
|
) -> Gateway:
|
|
37
|
-
body = CreateGatewayRequest(
|
|
38
|
-
name=gateway_name,
|
|
39
|
-
backend_type=backend_type,
|
|
40
|
-
region=region,
|
|
41
|
-
configuration=configuration,
|
|
42
|
-
)
|
|
31
|
+
body = CreateGatewayRequest(configuration=configuration)
|
|
43
32
|
resp = self._request(f"/api/project/{project_name}/gateways/create", body=body.json())
|
|
44
33
|
return parse_obj_as(Gateway.__response__, resp.json())
|
|
45
34
|
|
dstack/api/server/_repos.py
CHANGED
|
@@ -6,7 +6,6 @@ from dstack._internal.core.models.repos import AnyRepoInfo, RemoteRepoCreds, Rep
|
|
|
6
6
|
from dstack._internal.server.schemas.repos import (
|
|
7
7
|
DeleteReposRequest,
|
|
8
8
|
GetRepoRequest,
|
|
9
|
-
RemoteRepoCredsDto,
|
|
10
9
|
SaveRepoCredsRequest,
|
|
11
10
|
)
|
|
12
11
|
from dstack.api.server._group import APIClientGroup
|
|
@@ -32,9 +31,7 @@ class ReposAPIClient(APIClientGroup):
|
|
|
32
31
|
body = SaveRepoCredsRequest(
|
|
33
32
|
repo_id=repo_id,
|
|
34
33
|
repo_info=repo_info,
|
|
35
|
-
repo_creds=
|
|
36
|
-
if repo_creds
|
|
37
|
-
else None,
|
|
34
|
+
repo_creds=repo_creds,
|
|
38
35
|
)
|
|
39
36
|
self._request(f"/api/project/{project_name}/repos/init", body=body.json())
|
|
40
37
|
|