dstack 0.18.43__py3-none-any.whl → 0.19.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of dstack might be problematic. Click here for more details.
- dstack/_internal/cli/commands/gateway.py +15 -3
- dstack/_internal/cli/commands/logs.py +0 -22
- dstack/_internal/cli/commands/stats.py +8 -17
- dstack/_internal/cli/main.py +1 -5
- dstack/_internal/cli/services/configurators/fleet.py +4 -39
- dstack/_internal/cli/services/configurators/run.py +22 -20
- dstack/_internal/cli/services/profile.py +34 -83
- dstack/_internal/cli/utils/gateway.py +1 -1
- dstack/_internal/cli/utils/run.py +11 -0
- dstack/_internal/core/backends/__init__.py +56 -39
- dstack/_internal/core/backends/aws/__init__.py +0 -25
- dstack/_internal/core/backends/aws/auth.py +1 -10
- dstack/_internal/core/backends/aws/backend.py +26 -0
- dstack/_internal/core/backends/aws/compute.py +21 -45
- dstack/_internal/{server/services/backends/configurators/aws.py → core/backends/aws/configurator.py} +46 -85
- dstack/_internal/core/backends/aws/models.py +135 -0
- dstack/_internal/core/backends/aws/resources.py +1 -1
- dstack/_internal/core/backends/azure/__init__.py +0 -20
- dstack/_internal/core/backends/azure/auth.py +2 -11
- dstack/_internal/core/backends/azure/backend.py +21 -0
- dstack/_internal/core/backends/azure/compute.py +14 -28
- dstack/_internal/{server/services/backends/configurators/azure.py → core/backends/azure/configurator.py} +141 -210
- dstack/_internal/core/backends/azure/models.py +89 -0
- dstack/_internal/core/backends/base/__init__.py +0 -12
- dstack/_internal/core/backends/base/backend.py +18 -0
- dstack/_internal/core/backends/base/compute.py +153 -33
- dstack/_internal/core/backends/base/configurator.py +105 -0
- dstack/_internal/core/backends/base/models.py +14 -0
- dstack/_internal/core/backends/configurators.py +138 -0
- dstack/_internal/core/backends/cudo/__init__.py +0 -15
- dstack/_internal/core/backends/cudo/backend.py +16 -0
- dstack/_internal/core/backends/cudo/compute.py +8 -26
- dstack/_internal/core/backends/cudo/configurator.py +72 -0
- dstack/_internal/core/backends/cudo/models.py +37 -0
- dstack/_internal/core/backends/datacrunch/__init__.py +0 -15
- dstack/_internal/core/backends/datacrunch/backend.py +16 -0
- dstack/_internal/core/backends/datacrunch/compute.py +8 -25
- dstack/_internal/core/backends/datacrunch/configurator.py +66 -0
- dstack/_internal/core/backends/datacrunch/models.py +38 -0
- dstack/_internal/core/{models/backends/dstack.py → backends/dstack/models.py} +7 -7
- dstack/_internal/core/backends/gcp/__init__.py +0 -16
- dstack/_internal/core/backends/gcp/auth.py +2 -11
- dstack/_internal/core/backends/gcp/backend.py +17 -0
- dstack/_internal/core/backends/gcp/compute.py +14 -44
- dstack/_internal/{server/services/backends/configurators/gcp.py → core/backends/gcp/configurator.py} +46 -103
- dstack/_internal/core/backends/gcp/models.py +125 -0
- dstack/_internal/core/backends/kubernetes/__init__.py +0 -15
- dstack/_internal/core/backends/kubernetes/backend.py +16 -0
- dstack/_internal/core/backends/kubernetes/compute.py +16 -5
- dstack/_internal/core/backends/kubernetes/configurator.py +55 -0
- dstack/_internal/core/backends/kubernetes/models.py +72 -0
- dstack/_internal/core/backends/lambdalabs/__init__.py +0 -16
- dstack/_internal/core/backends/lambdalabs/backend.py +17 -0
- dstack/_internal/core/backends/lambdalabs/compute.py +7 -28
- dstack/_internal/core/backends/lambdalabs/configurator.py +82 -0
- dstack/_internal/core/backends/lambdalabs/models.py +37 -0
- dstack/_internal/core/backends/local/__init__.py +0 -13
- dstack/_internal/core/backends/local/backend.py +14 -0
- dstack/_internal/core/backends/local/compute.py +16 -2
- dstack/_internal/core/backends/models.py +128 -0
- dstack/_internal/core/backends/oci/__init__.py +0 -15
- dstack/_internal/core/backends/oci/auth.py +1 -5
- dstack/_internal/core/backends/oci/backend.py +16 -0
- dstack/_internal/core/backends/oci/compute.py +9 -23
- dstack/_internal/{server/services/backends/configurators/oci.py → core/backends/oci/configurator.py} +40 -85
- dstack/_internal/core/{models/backends/oci.py → backends/oci/models.py} +24 -25
- dstack/_internal/core/backends/oci/region.py +1 -1
- dstack/_internal/core/backends/runpod/__init__.py +0 -15
- dstack/_internal/core/backends/runpod/backend.py +16 -0
- dstack/_internal/core/backends/runpod/compute.py +28 -6
- dstack/_internal/core/backends/runpod/configurator.py +59 -0
- dstack/_internal/core/backends/runpod/models.py +54 -0
- dstack/_internal/core/backends/template/__init__.py +0 -0
- dstack/_internal/core/backends/tensordock/__init__.py +0 -15
- dstack/_internal/core/backends/tensordock/backend.py +16 -0
- dstack/_internal/core/backends/tensordock/compute.py +8 -27
- dstack/_internal/core/backends/tensordock/configurator.py +68 -0
- dstack/_internal/core/backends/tensordock/models.py +38 -0
- dstack/_internal/core/backends/vastai/__init__.py +0 -15
- dstack/_internal/core/backends/vastai/backend.py +16 -0
- dstack/_internal/core/backends/vastai/compute.py +2 -2
- dstack/_internal/core/backends/vastai/configurator.py +66 -0
- dstack/_internal/core/backends/vastai/models.py +37 -0
- dstack/_internal/core/backends/vultr/__init__.py +0 -15
- dstack/_internal/core/backends/vultr/backend.py +16 -0
- dstack/_internal/core/backends/vultr/compute.py +10 -24
- dstack/_internal/core/backends/vultr/configurator.py +64 -0
- dstack/_internal/core/backends/vultr/models.py +34 -0
- dstack/_internal/core/models/backends/__init__.py +0 -184
- dstack/_internal/core/models/backends/base.py +0 -19
- dstack/_internal/core/models/configurations.py +22 -16
- dstack/_internal/core/models/envs.py +4 -3
- dstack/_internal/core/models/fleets.py +17 -22
- dstack/_internal/core/models/gateways.py +3 -3
- dstack/_internal/core/models/instances.py +24 -0
- dstack/_internal/core/models/profiles.py +85 -45
- dstack/_internal/core/models/projects.py +1 -1
- dstack/_internal/core/models/repos/base.py +0 -5
- dstack/_internal/core/models/repos/local.py +3 -3
- dstack/_internal/core/models/repos/remote.py +26 -12
- dstack/_internal/core/models/repos/virtual.py +1 -1
- dstack/_internal/core/models/resources.py +45 -76
- dstack/_internal/core/models/runs.py +21 -19
- dstack/_internal/core/models/volumes.py +1 -3
- dstack/_internal/core/services/profiles.py +7 -16
- dstack/_internal/core/services/repos.py +0 -4
- dstack/_internal/server/app.py +11 -4
- dstack/_internal/server/background/__init__.py +10 -0
- dstack/_internal/server/background/tasks/process_gateways.py +4 -8
- dstack/_internal/server/background/tasks/process_instances.py +14 -9
- dstack/_internal/server/background/tasks/process_metrics.py +1 -1
- dstack/_internal/server/background/tasks/process_placement_groups.py +5 -1
- dstack/_internal/server/background/tasks/process_prometheus_metrics.py +135 -0
- dstack/_internal/server/background/tasks/process_running_jobs.py +80 -24
- dstack/_internal/server/background/tasks/process_runs.py +1 -0
- dstack/_internal/server/background/tasks/process_submitted_jobs.py +20 -38
- dstack/_internal/server/background/tasks/process_volumes.py +5 -2
- dstack/_internal/server/migrations/versions/60e444118b6d_add_jobprometheusmetrics.py +40 -0
- dstack/_internal/server/migrations/versions/7bc2586e8b9e_make_instancemodel_pool_id_optional.py +36 -0
- dstack/_internal/server/migrations/versions/98d1b92988bc_add_jobterminationreason_terminated_due_.py +140 -0
- dstack/_internal/server/migrations/versions/bc8ca4a505c6_store_backendtype_as_string.py +171 -0
- dstack/_internal/server/models.py +59 -9
- dstack/_internal/server/routers/backends.py +14 -23
- dstack/_internal/server/routers/instances.py +3 -4
- dstack/_internal/server/routers/metrics.py +31 -10
- dstack/_internal/server/routers/prometheus.py +36 -0
- dstack/_internal/server/routers/repos.py +1 -2
- dstack/_internal/server/routers/runs.py +13 -59
- dstack/_internal/server/schemas/gateways.py +14 -23
- dstack/_internal/server/schemas/projects.py +7 -2
- dstack/_internal/server/schemas/repos.py +2 -38
- dstack/_internal/server/schemas/runner.py +1 -0
- dstack/_internal/server/schemas/runs.py +1 -24
- dstack/_internal/server/security/permissions.py +1 -1
- dstack/_internal/server/services/backends/__init__.py +85 -158
- dstack/_internal/server/services/config.py +53 -567
- dstack/_internal/server/services/fleets.py +9 -103
- dstack/_internal/server/services/gateways/__init__.py +13 -4
- dstack/_internal/server/services/{pools.py → instances.py} +22 -329
- dstack/_internal/server/services/jobs/__init__.py +9 -6
- dstack/_internal/server/services/jobs/configurators/base.py +25 -1
- dstack/_internal/server/services/jobs/configurators/dev.py +9 -1
- dstack/_internal/server/services/jobs/configurators/extensions/cursor.py +42 -0
- dstack/_internal/server/services/metrics.py +131 -72
- dstack/_internal/server/services/offers.py +1 -1
- dstack/_internal/server/services/projects.py +23 -14
- dstack/_internal/server/services/prometheus.py +245 -0
- dstack/_internal/server/services/runner/client.py +14 -3
- dstack/_internal/server/services/runs.py +67 -31
- dstack/_internal/server/services/volumes.py +9 -4
- dstack/_internal/server/settings.py +3 -0
- dstack/_internal/server/statics/index.html +1 -1
- dstack/_internal/server/statics/{main-fe8fd9db55df8d10e648.js → main-4a0fe83e84574654e397.js} +76 -19
- dstack/_internal/server/statics/{main-fe8fd9db55df8d10e648.js.map → main-4a0fe83e84574654e397.js.map} +1 -1
- dstack/_internal/server/statics/{main-7510e71dfa9749a4e70e.css → main-da9f8c06a69c20dac23e.css} +1 -1
- dstack/_internal/server/statics/static/media/entraID.d65d1f3e9486a8e56d24fc07b3230885.svg +9 -0
- dstack/_internal/server/testing/common.py +75 -32
- dstack/_internal/utils/json_schema.py +6 -0
- dstack/_internal/utils/ssh.py +2 -1
- dstack/api/__init__.py +4 -0
- dstack/api/_public/__init__.py +16 -20
- dstack/api/_public/backends.py +1 -1
- dstack/api/_public/repos.py +36 -36
- dstack/api/_public/runs.py +170 -83
- dstack/api/server/__init__.py +11 -13
- dstack/api/server/_backends.py +12 -16
- dstack/api/server/_fleets.py +15 -55
- dstack/api/server/_gateways.py +3 -14
- dstack/api/server/_repos.py +1 -4
- dstack/api/server/_runs.py +21 -96
- dstack/api/server/_volumes.py +10 -5
- dstack/api/utils.py +3 -0
- dstack/version.py +1 -1
- {dstack-0.18.43.dist-info → dstack-0.19.0.dist-info}/METADATA +10 -1
- {dstack-0.18.43.dist-info → dstack-0.19.0.dist-info}/RECORD +229 -206
- tests/_internal/cli/services/configurators/test_profile.py +6 -6
- tests/_internal/core/backends/aws/test_configurator.py +35 -0
- tests/_internal/core/backends/aws/test_resources.py +1 -1
- tests/_internal/core/backends/azure/test_configurator.py +61 -0
- tests/_internal/core/backends/cudo/__init__.py +0 -0
- tests/_internal/core/backends/cudo/test_configurator.py +37 -0
- tests/_internal/core/backends/datacrunch/__init__.py +0 -0
- tests/_internal/core/backends/datacrunch/test_configurator.py +17 -0
- tests/_internal/core/backends/gcp/test_configurator.py +42 -0
- tests/_internal/core/backends/kubernetes/test_configurator.py +43 -0
- tests/_internal/core/backends/lambdalabs/__init__.py +0 -0
- tests/_internal/core/backends/lambdalabs/test_configurator.py +38 -0
- tests/_internal/core/backends/oci/test_configurator.py +55 -0
- tests/_internal/core/backends/runpod/__init__.py +0 -0
- tests/_internal/core/backends/runpod/test_configurator.py +33 -0
- tests/_internal/core/backends/tensordock/__init__.py +0 -0
- tests/_internal/core/backends/tensordock/test_configurator.py +38 -0
- tests/_internal/core/backends/vastai/__init__.py +0 -0
- tests/_internal/core/backends/vastai/test_configurator.py +33 -0
- tests/_internal/core/backends/vultr/__init__.py +0 -0
- tests/_internal/core/backends/vultr/test_configurator.py +33 -0
- tests/_internal/server/background/tasks/test_process_gateways.py +4 -0
- tests/_internal/server/background/tasks/test_process_instances.py +49 -48
- tests/_internal/server/background/tasks/test_process_metrics.py +0 -3
- tests/_internal/server/background/tasks/test_process_placement_groups.py +2 -0
- tests/_internal/server/background/tasks/test_process_prometheus_metrics.py +186 -0
- tests/_internal/server/background/tasks/test_process_running_jobs.py +123 -19
- tests/_internal/server/background/tasks/test_process_runs.py +8 -22
- tests/_internal/server/background/tasks/test_process_submitted_jobs.py +3 -40
- tests/_internal/server/background/tasks/test_process_submitted_volumes.py +2 -0
- tests/_internal/server/background/tasks/test_process_terminating_jobs.py +10 -15
- tests/_internal/server/routers/test_backends.py +6 -764
- tests/_internal/server/routers/test_fleets.py +2 -26
- tests/_internal/server/routers/test_gateways.py +27 -3
- tests/_internal/server/routers/test_instances.py +0 -10
- tests/_internal/server/routers/test_metrics.py +42 -0
- tests/_internal/server/routers/test_projects.py +56 -0
- tests/_internal/server/routers/test_prometheus.py +333 -0
- tests/_internal/server/routers/test_repos.py +0 -15
- tests/_internal/server/routers/test_runs.py +83 -275
- tests/_internal/server/routers/test_volumes.py +2 -3
- tests/_internal/server/services/backends/__init__.py +0 -0
- tests/_internal/server/services/jobs/configurators/test_task.py +35 -0
- tests/_internal/server/services/test_config.py +7 -4
- tests/_internal/server/services/test_fleets.py +1 -4
- tests/_internal/server/services/{test_pools.py → test_instances.py} +11 -49
- tests/_internal/server/services/test_metrics.py +167 -0
- tests/_internal/server/services/test_repos.py +1 -14
- tests/_internal/server/services/test_runs.py +0 -4
- dstack/_internal/cli/commands/pool.py +0 -581
- dstack/_internal/cli/commands/run.py +0 -75
- dstack/_internal/core/backends/aws/config.py +0 -18
- dstack/_internal/core/backends/azure/config.py +0 -12
- dstack/_internal/core/backends/base/config.py +0 -5
- dstack/_internal/core/backends/cudo/config.py +0 -9
- dstack/_internal/core/backends/datacrunch/config.py +0 -9
- dstack/_internal/core/backends/gcp/config.py +0 -22
- dstack/_internal/core/backends/kubernetes/config.py +0 -6
- dstack/_internal/core/backends/lambdalabs/config.py +0 -9
- dstack/_internal/core/backends/nebius/__init__.py +0 -15
- dstack/_internal/core/backends/nebius/api_client.py +0 -319
- dstack/_internal/core/backends/nebius/compute.py +0 -220
- dstack/_internal/core/backends/nebius/config.py +0 -6
- dstack/_internal/core/backends/nebius/types.py +0 -37
- dstack/_internal/core/backends/oci/config.py +0 -6
- dstack/_internal/core/backends/runpod/config.py +0 -9
- dstack/_internal/core/backends/tensordock/config.py +0 -9
- dstack/_internal/core/backends/vastai/config.py +0 -6
- dstack/_internal/core/backends/vultr/config.py +0 -9
- dstack/_internal/core/models/backends/aws.py +0 -86
- dstack/_internal/core/models/backends/azure.py +0 -68
- dstack/_internal/core/models/backends/cudo.py +0 -43
- dstack/_internal/core/models/backends/datacrunch.py +0 -44
- dstack/_internal/core/models/backends/gcp.py +0 -67
- dstack/_internal/core/models/backends/kubernetes.py +0 -40
- dstack/_internal/core/models/backends/lambdalabs.py +0 -43
- dstack/_internal/core/models/backends/nebius.py +0 -54
- dstack/_internal/core/models/backends/runpod.py +0 -40
- dstack/_internal/core/models/backends/tensordock.py +0 -44
- dstack/_internal/core/models/backends/vastai.py +0 -43
- dstack/_internal/core/models/backends/vultr.py +0 -40
- dstack/_internal/core/models/pools.py +0 -43
- dstack/_internal/server/routers/pools.py +0 -142
- dstack/_internal/server/schemas/pools.py +0 -38
- dstack/_internal/server/services/backends/configurators/base.py +0 -72
- dstack/_internal/server/services/backends/configurators/cudo.py +0 -87
- dstack/_internal/server/services/backends/configurators/datacrunch.py +0 -79
- dstack/_internal/server/services/backends/configurators/kubernetes.py +0 -63
- dstack/_internal/server/services/backends/configurators/lambdalabs.py +0 -98
- dstack/_internal/server/services/backends/configurators/nebius.py +0 -85
- dstack/_internal/server/services/backends/configurators/runpod.py +0 -97
- dstack/_internal/server/services/backends/configurators/tensordock.py +0 -82
- dstack/_internal/server/services/backends/configurators/vastai.py +0 -80
- dstack/_internal/server/services/backends/configurators/vultr.py +0 -80
- dstack/api/_public/pools.py +0 -41
- dstack/api/_public/resources.py +0 -105
- dstack/api/server/_pools.py +0 -63
- tests/_internal/server/routers/test_pools.py +0 -612
- /dstack/_internal/{server/services/backends/configurators → core/backends/dstack}/__init__.py +0 -0
- {dstack-0.18.43.dist-info → dstack-0.19.0.dist-info}/LICENSE.md +0 -0
- {dstack-0.18.43.dist-info → dstack-0.19.0.dist-info}/WHEEL +0 -0
- {dstack-0.18.43.dist-info → dstack-0.19.0.dist-info}/entry_points.txt +0 -0
- {dstack-0.18.43.dist-info → dstack-0.19.0.dist-info}/top_level.txt +0 -0
|
@@ -19,39 +19,34 @@ from azure.mgmt.network.models import (
|
|
|
19
19
|
)
|
|
20
20
|
from azure.mgmt.resource.resources.models import ResourceGroup
|
|
21
21
|
|
|
22
|
-
from dstack._internal.core.backends.azure import
|
|
22
|
+
from dstack._internal.core.backends.azure import auth, compute, resources
|
|
23
23
|
from dstack._internal.core.backends.azure import utils as azure_utils
|
|
24
|
-
from dstack._internal.core.backends.azure.
|
|
25
|
-
from dstack._internal.core.
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
)
|
|
30
|
-
from dstack._internal.core.models.backends.azure import (
|
|
31
|
-
AnyAzureConfigInfo,
|
|
24
|
+
from dstack._internal.core.backends.azure.backend import AzureBackend
|
|
25
|
+
from dstack._internal.core.backends.azure.models import (
|
|
26
|
+
AnyAzureBackendConfig,
|
|
27
|
+
AzureBackendConfig,
|
|
28
|
+
AzureBackendConfigWithCreds,
|
|
32
29
|
AzureClientCreds,
|
|
33
|
-
|
|
34
|
-
AzureConfigInfoWithCreds,
|
|
35
|
-
AzureConfigInfoWithCredsPartial,
|
|
36
|
-
AzureConfigValues,
|
|
30
|
+
AzureConfig,
|
|
37
31
|
AzureCreds,
|
|
38
32
|
AzureDefaultCreds,
|
|
39
33
|
AzureStoredConfig,
|
|
40
34
|
)
|
|
41
|
-
from dstack._internal.core.
|
|
42
|
-
BackendType,
|
|
43
|
-
ConfigElement,
|
|
44
|
-
ConfigElementValue,
|
|
45
|
-
ConfigMultiElement,
|
|
46
|
-
)
|
|
47
|
-
from dstack._internal.core.models.common import is_core_model_instance
|
|
48
|
-
from dstack._internal.server import settings
|
|
49
|
-
from dstack._internal.server.models import BackendModel, DecryptedString, ProjectModel
|
|
50
|
-
from dstack._internal.server.services.backends.configurators.base import (
|
|
35
|
+
from dstack._internal.core.backends.base.configurator import (
|
|
51
36
|
TAGS_MAX_NUM,
|
|
37
|
+
BackendRecord,
|
|
52
38
|
Configurator,
|
|
53
39
|
raise_invalid_credentials_error,
|
|
54
40
|
)
|
|
41
|
+
from dstack._internal.core.errors import (
|
|
42
|
+
BackendAuthError,
|
|
43
|
+
BackendError,
|
|
44
|
+
ServerClientError,
|
|
45
|
+
)
|
|
46
|
+
from dstack._internal.core.models.backends.base import (
|
|
47
|
+
BackendType,
|
|
48
|
+
)
|
|
49
|
+
from dstack._internal.core.models.common import is_core_model_instance
|
|
55
50
|
|
|
56
51
|
LOCATIONS = [
|
|
57
52
|
("(US) Central US", "centralus"),
|
|
@@ -77,47 +72,16 @@ MAIN_LOCATION = "eastus"
|
|
|
77
72
|
|
|
78
73
|
|
|
79
74
|
class AzureConfigurator(Configurator):
|
|
80
|
-
TYPE
|
|
81
|
-
|
|
82
|
-
def get_default_configs(self) -> List[AzureConfigInfoWithCreds]:
|
|
83
|
-
if not auth.default_creds_available():
|
|
84
|
-
return []
|
|
85
|
-
try:
|
|
86
|
-
credential, _ = auth.authenticate(AzureDefaultCreds())
|
|
87
|
-
except BackendAuthError:
|
|
88
|
-
return []
|
|
89
|
-
tenant_id_element = self._get_tenant_id_element(credential=credential)
|
|
90
|
-
tenant_ids = [v.value for v in tenant_id_element.values]
|
|
91
|
-
subscription_id_element = self._get_subscription_id_element(credential=credential)
|
|
92
|
-
subscription_ids = [v.value for v in subscription_id_element.values]
|
|
93
|
-
configs = []
|
|
94
|
-
for tenant_id in tenant_ids:
|
|
95
|
-
for subscription_id in subscription_ids:
|
|
96
|
-
config = AzureConfigInfoWithCreds(
|
|
97
|
-
tenant_id=tenant_id,
|
|
98
|
-
subscription_id=subscription_id,
|
|
99
|
-
locations=DEFAULT_LOCATIONS,
|
|
100
|
-
creds=AzureDefaultCreds(),
|
|
101
|
-
)
|
|
102
|
-
configs.append(config)
|
|
103
|
-
return configs
|
|
75
|
+
TYPE = BackendType.AZURE
|
|
76
|
+
BACKEND_CLASS = AzureBackend
|
|
104
77
|
|
|
105
|
-
def
|
|
106
|
-
|
|
107
|
-
config_values.default_creds = (
|
|
108
|
-
settings.DEFAULT_CREDS_ENABLED and auth.default_creds_available()
|
|
109
|
-
)
|
|
110
|
-
if config.creds is None:
|
|
111
|
-
return config_values
|
|
112
|
-
if (
|
|
113
|
-
is_core_model_instance(config.creds, AzureDefaultCreds)
|
|
114
|
-
and not settings.DEFAULT_CREDS_ENABLED
|
|
115
|
-
):
|
|
78
|
+
def validate_config(self, config: AzureBackendConfigWithCreds, default_creds_enabled: bool):
|
|
79
|
+
if is_core_model_instance(config.creds, AzureDefaultCreds) and not default_creds_enabled:
|
|
116
80
|
raise_invalid_credentials_error(fields=[["creds"]])
|
|
117
81
|
if is_core_model_instance(config.creds, AzureClientCreds):
|
|
118
82
|
self._set_client_creds_tenant_id(config.creds, config.tenant_id)
|
|
119
83
|
try:
|
|
120
|
-
credential,
|
|
84
|
+
credential, _ = auth.authenticate(config.creds)
|
|
121
85
|
except BackendAuthError:
|
|
122
86
|
if is_core_model_instance(config.creds, AzureClientCreds):
|
|
123
87
|
raise_invalid_credentials_error(
|
|
@@ -129,29 +93,18 @@ class AzureConfigurator(Configurator):
|
|
|
129
93
|
)
|
|
130
94
|
else:
|
|
131
95
|
raise_invalid_credentials_error(fields=[["creds"]])
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
)
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
config_values.subscription_id = self._get_subscription_id_element(
|
|
139
|
-
credential=credential,
|
|
140
|
-
selected=config.subscription_id,
|
|
141
|
-
)
|
|
142
|
-
if config_values.subscription_id.selected is None:
|
|
143
|
-
return config_values
|
|
144
|
-
config_values.locations = self._get_locations_element(
|
|
145
|
-
selected=config.locations or DEFAULT_LOCATIONS
|
|
146
|
-
)
|
|
147
|
-
self._check_config(config=config, credential=credential)
|
|
148
|
-
return config_values
|
|
96
|
+
self._check_config_tenant_id(config=config, credential=credential)
|
|
97
|
+
self._check_config_subscription_id(config=config, credential=credential)
|
|
98
|
+
self._check_config_locations(config)
|
|
99
|
+
self._check_config_tags(config)
|
|
100
|
+
self._check_config_resource_group(config=config, credential=credential)
|
|
101
|
+
self._check_config_vpc(config=config, credential=credential)
|
|
149
102
|
|
|
150
103
|
def create_backend(
|
|
151
|
-
self,
|
|
152
|
-
) ->
|
|
153
|
-
if config.
|
|
154
|
-
config.
|
|
104
|
+
self, project_name: str, config: AzureBackendConfigWithCreds
|
|
105
|
+
) -> BackendRecord:
|
|
106
|
+
if config.regions is None:
|
|
107
|
+
config.regions = DEFAULT_LOCATIONS
|
|
155
108
|
if is_core_model_instance(config.creds, AzureClientCreds):
|
|
156
109
|
self._set_client_creds_tenant_id(config.creds, config.tenant_id)
|
|
157
110
|
credential, _ = auth.authenticate(config.creds)
|
|
@@ -160,173 +113,85 @@ class AzureConfigurator(Configurator):
|
|
|
160
113
|
credential=credential,
|
|
161
114
|
subscription_id=config.subscription_id,
|
|
162
115
|
location=MAIN_LOCATION,
|
|
163
|
-
project_name=
|
|
116
|
+
project_name=project_name,
|
|
164
117
|
)
|
|
165
118
|
self._create_network_resources(
|
|
166
119
|
credential=credential,
|
|
167
120
|
subscription_id=config.subscription_id,
|
|
168
121
|
resource_group=config.resource_group,
|
|
169
|
-
locations=config.
|
|
122
|
+
locations=config.regions,
|
|
170
123
|
create_default_network=config.vpc_ids is None,
|
|
171
124
|
)
|
|
172
|
-
return
|
|
173
|
-
project_id=project.id,
|
|
174
|
-
type=self.TYPE.value,
|
|
125
|
+
return BackendRecord(
|
|
175
126
|
config=AzureStoredConfig(
|
|
176
|
-
**
|
|
127
|
+
**AzureBackendConfig.__response__.parse_obj(config).dict()
|
|
177
128
|
).json(),
|
|
178
|
-
auth=
|
|
129
|
+
auth=AzureCreds.parse_obj(config.creds).__root__.json(),
|
|
179
130
|
)
|
|
180
131
|
|
|
181
|
-
def
|
|
182
|
-
|
|
132
|
+
def get_backend_config(
|
|
133
|
+
self, record: BackendRecord, include_creds: bool
|
|
134
|
+
) -> AnyAzureBackendConfig:
|
|
135
|
+
config = self._get_config(record)
|
|
183
136
|
if include_creds:
|
|
184
|
-
return
|
|
185
|
-
return
|
|
137
|
+
return AzureBackendConfigWithCreds.__response__.parse_obj(config)
|
|
138
|
+
return AzureBackendConfig.__response__.parse_obj(config)
|
|
186
139
|
|
|
187
|
-
def get_backend(self,
|
|
188
|
-
config = self.
|
|
140
|
+
def get_backend(self, record: BackendRecord) -> AzureBackend:
|
|
141
|
+
config = self._get_config(record)
|
|
189
142
|
return AzureBackend(config=config)
|
|
190
143
|
|
|
191
|
-
def
|
|
144
|
+
def _get_config(self, record: BackendRecord) -> AzureConfig:
|
|
145
|
+
config_dict = json.loads(record.config)
|
|
146
|
+
regions = config_dict.pop("regions", None)
|
|
147
|
+
if regions is None:
|
|
148
|
+
# Legacy config stores regions as locations
|
|
149
|
+
regions = config_dict.pop("locations")
|
|
192
150
|
return AzureConfig.__response__(
|
|
193
|
-
**
|
|
194
|
-
|
|
151
|
+
**config_dict,
|
|
152
|
+
regions=regions,
|
|
153
|
+
creds=AzureCreds.parse_raw(record.auth).__root__,
|
|
195
154
|
)
|
|
196
155
|
|
|
197
|
-
def
|
|
198
|
-
self,
|
|
199
|
-
creds: AzureClientCreds,
|
|
200
|
-
tenant_id: Optional[str],
|
|
156
|
+
def _check_config_tenant_id(
|
|
157
|
+
self, config: AzureBackendConfigWithCreds, credential: auth.AzureCredential
|
|
201
158
|
):
|
|
202
|
-
if creds.tenant_id is not None:
|
|
203
|
-
return
|
|
204
|
-
if tenant_id is None:
|
|
205
|
-
raise_invalid_credentials_error(
|
|
206
|
-
fields=[
|
|
207
|
-
["creds", "tenant_id"],
|
|
208
|
-
["tenant_id"],
|
|
209
|
-
]
|
|
210
|
-
)
|
|
211
|
-
creds.tenant_id = tenant_id
|
|
212
|
-
|
|
213
|
-
def _get_tenant_id_element(
|
|
214
|
-
self,
|
|
215
|
-
credential: auth.AzureCredential,
|
|
216
|
-
selected: Optional[str] = None,
|
|
217
|
-
) -> ConfigElement:
|
|
218
159
|
subscription_client = subscription_mgmt.SubscriptionClient(credential=credential)
|
|
219
|
-
element = ConfigElement(selected=selected)
|
|
220
160
|
tenant_ids = []
|
|
221
161
|
for tenant in subscription_client.tenants.list():
|
|
222
162
|
tenant_ids.append(tenant.tenant_id)
|
|
223
|
-
|
|
224
|
-
ConfigElementValue(value=tenant.tenant_id, label=tenant.tenant_id)
|
|
225
|
-
)
|
|
226
|
-
if selected is not None and selected not in tenant_ids:
|
|
163
|
+
if config.tenant_id not in tenant_ids:
|
|
227
164
|
raise ServerClientError(
|
|
228
165
|
"Invalid tenant_id",
|
|
229
166
|
fields=[["tenant_id"]],
|
|
230
167
|
)
|
|
231
|
-
if len(tenant_ids) == 1:
|
|
232
|
-
element.selected = tenant_ids[0]
|
|
233
|
-
return element
|
|
234
168
|
|
|
235
|
-
def
|
|
236
|
-
self,
|
|
237
|
-
|
|
238
|
-
selected: Optional[str] = None,
|
|
239
|
-
) -> ConfigElement:
|
|
169
|
+
def _check_config_subscription_id(
|
|
170
|
+
self, config: AzureBackendConfigWithCreds, credential: auth.AzureCredential
|
|
171
|
+
):
|
|
240
172
|
subscription_client = subscription_mgmt.SubscriptionClient(credential=credential)
|
|
241
|
-
element = ConfigElement(selected=selected)
|
|
242
173
|
subscription_ids = []
|
|
243
174
|
for subscription in subscription_client.subscriptions.list():
|
|
244
175
|
subscription_ids.append(subscription.subscription_id)
|
|
245
|
-
|
|
246
|
-
ConfigElementValue(
|
|
247
|
-
value=subscription.subscription_id,
|
|
248
|
-
label=f"{subscription.display_name} ({subscription.subscription_id})",
|
|
249
|
-
)
|
|
250
|
-
)
|
|
251
|
-
if selected is not None and selected not in subscription_ids:
|
|
176
|
+
if config.subscription_id not in subscription_ids:
|
|
252
177
|
raise ServerClientError(
|
|
253
178
|
"Invalid subscription_id",
|
|
254
179
|
fields=[["subscription_id"]],
|
|
255
180
|
)
|
|
256
|
-
if len(subscription_ids) == 1:
|
|
257
|
-
element.selected = subscription_ids[0]
|
|
258
181
|
if len(subscription_ids) == 0:
|
|
259
182
|
# Credentials without granted roles don't see any subscriptions
|
|
260
183
|
raise ServerClientError(
|
|
261
184
|
msg="No Azure subscriptions found for provided credentials. Ensure the account has enough permissions.",
|
|
262
185
|
)
|
|
263
|
-
return element
|
|
264
|
-
|
|
265
|
-
def _get_locations_element(self, selected: List[str]) -> ConfigMultiElement:
|
|
266
|
-
element = ConfigMultiElement()
|
|
267
|
-
for loc in LOCATION_VALUES:
|
|
268
|
-
element.values.append(ConfigElementValue(value=loc, label=loc))
|
|
269
|
-
element.selected = selected
|
|
270
|
-
return element
|
|
271
|
-
|
|
272
|
-
def _create_resource_group(
|
|
273
|
-
self,
|
|
274
|
-
credential: auth.AzureCredential,
|
|
275
|
-
subscription_id: str,
|
|
276
|
-
location: str,
|
|
277
|
-
project_name: str,
|
|
278
|
-
) -> str:
|
|
279
|
-
resource_manager = ResourceManager(
|
|
280
|
-
credential=credential,
|
|
281
|
-
subscription_id=subscription_id,
|
|
282
|
-
)
|
|
283
|
-
return resource_manager.create_resource_group(
|
|
284
|
-
name=_get_resource_group_name(project_name),
|
|
285
|
-
location=location,
|
|
286
|
-
)
|
|
287
|
-
|
|
288
|
-
def _create_network_resources(
|
|
289
|
-
self,
|
|
290
|
-
credential: auth.AzureCredential,
|
|
291
|
-
subscription_id: str,
|
|
292
|
-
resource_group: str,
|
|
293
|
-
locations: List[str],
|
|
294
|
-
create_default_network: bool,
|
|
295
|
-
):
|
|
296
|
-
def func(location: str):
|
|
297
|
-
network_manager = NetworkManager(
|
|
298
|
-
credential=credential, subscription_id=subscription_id
|
|
299
|
-
)
|
|
300
|
-
if create_default_network:
|
|
301
|
-
network_manager.create_virtual_network(
|
|
302
|
-
resource_group=resource_group,
|
|
303
|
-
location=location,
|
|
304
|
-
name=azure_utils.get_default_network_name(resource_group, location),
|
|
305
|
-
subnet_name=azure_utils.get_default_subnet_name(resource_group, location),
|
|
306
|
-
)
|
|
307
|
-
network_manager.create_network_security_group(
|
|
308
|
-
resource_group=resource_group,
|
|
309
|
-
location=location,
|
|
310
|
-
name=azure_utils.get_default_network_security_group_name(resource_group, location),
|
|
311
|
-
)
|
|
312
|
-
network_manager.create_gateway_network_security_group(
|
|
313
|
-
resource_group=resource_group,
|
|
314
|
-
location=location,
|
|
315
|
-
name=azure_utils.get_gateway_network_security_group_name(resource_group, location),
|
|
316
|
-
)
|
|
317
|
-
|
|
318
|
-
with ThreadPoolExecutor(max_workers=8) as executor:
|
|
319
|
-
for location in locations:
|
|
320
|
-
executor.submit(func, location)
|
|
321
186
|
|
|
322
|
-
def
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
187
|
+
def _check_config_locations(self, config: AzureBackendConfigWithCreds):
|
|
188
|
+
if config.regions is None:
|
|
189
|
+
return
|
|
190
|
+
for location in config.regions:
|
|
191
|
+
if location not in LOCATION_VALUES:
|
|
192
|
+
raise ServerClientError(f"Unknown Azure location {location}")
|
|
328
193
|
|
|
329
|
-
def
|
|
194
|
+
def _check_config_tags(self, config: AzureBackendConfigWithCreds):
|
|
330
195
|
if not config.tags:
|
|
331
196
|
return
|
|
332
197
|
if len(config.tags) > TAGS_MAX_NUM:
|
|
@@ -338,8 +203,8 @@ class AzureConfigurator(Configurator):
|
|
|
338
203
|
except BackendError as e:
|
|
339
204
|
raise ServerClientError(e.args[0])
|
|
340
205
|
|
|
341
|
-
def
|
|
342
|
-
self, config:
|
|
206
|
+
def _check_config_resource_group(
|
|
207
|
+
self, config: AzureBackendConfigWithCreds, credential: auth.AzureCredential
|
|
343
208
|
):
|
|
344
209
|
if config.resource_group is None:
|
|
345
210
|
return
|
|
@@ -350,22 +215,22 @@ class AzureConfigurator(Configurator):
|
|
|
350
215
|
if not resource_manager.resource_group_exists(config.resource_group):
|
|
351
216
|
raise ServerClientError(f"Resource group {config.resource_group} not found")
|
|
352
217
|
|
|
353
|
-
def
|
|
354
|
-
self, config:
|
|
218
|
+
def _check_config_vpc(
|
|
219
|
+
self, config: AzureBackendConfigWithCreds, credential: auth.AzureCredential
|
|
355
220
|
):
|
|
356
221
|
if config.subscription_id is None:
|
|
357
222
|
return None
|
|
358
223
|
allocate_public_ip = config.public_ips if config.public_ips is not None else True
|
|
359
224
|
if config.public_ips is False and config.vpc_ids is None:
|
|
360
225
|
raise ServerClientError(msg="`vpc_ids` must be specified if `public_ips: false`.")
|
|
361
|
-
locations = config.
|
|
226
|
+
locations = config.regions
|
|
362
227
|
if locations is None:
|
|
363
228
|
locations = DEFAULT_LOCATIONS
|
|
364
229
|
if config.vpc_ids is not None:
|
|
365
230
|
vpc_ids_locations = list(config.vpc_ids.keys())
|
|
366
231
|
not_configured_locations = [loc for loc in locations if loc not in vpc_ids_locations]
|
|
367
232
|
if len(not_configured_locations) > 0:
|
|
368
|
-
if config.
|
|
233
|
+
if config.regions is None:
|
|
369
234
|
raise ServerClientError(
|
|
370
235
|
f"`vpc_ids` not configured for regions {not_configured_locations}. "
|
|
371
236
|
"Configure `vpc_ids` for all regions or specify `regions`."
|
|
@@ -396,6 +261,72 @@ class AzureConfigurator(Configurator):
|
|
|
396
261
|
except BackendError as e:
|
|
397
262
|
raise ServerClientError(e.args[0])
|
|
398
263
|
|
|
264
|
+
def _set_client_creds_tenant_id(
|
|
265
|
+
self,
|
|
266
|
+
creds: AzureClientCreds,
|
|
267
|
+
tenant_id: Optional[str],
|
|
268
|
+
):
|
|
269
|
+
if creds.tenant_id is not None:
|
|
270
|
+
return
|
|
271
|
+
if tenant_id is None:
|
|
272
|
+
raise_invalid_credentials_error(
|
|
273
|
+
fields=[
|
|
274
|
+
["creds", "tenant_id"],
|
|
275
|
+
["tenant_id"],
|
|
276
|
+
]
|
|
277
|
+
)
|
|
278
|
+
creds.tenant_id = tenant_id
|
|
279
|
+
|
|
280
|
+
def _create_resource_group(
|
|
281
|
+
self,
|
|
282
|
+
credential: auth.AzureCredential,
|
|
283
|
+
subscription_id: str,
|
|
284
|
+
location: str,
|
|
285
|
+
project_name: str,
|
|
286
|
+
) -> str:
|
|
287
|
+
resource_manager = ResourceManager(
|
|
288
|
+
credential=credential,
|
|
289
|
+
subscription_id=subscription_id,
|
|
290
|
+
)
|
|
291
|
+
return resource_manager.create_resource_group(
|
|
292
|
+
name=_get_resource_group_name(project_name),
|
|
293
|
+
location=location,
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
def _create_network_resources(
|
|
297
|
+
self,
|
|
298
|
+
credential: auth.AzureCredential,
|
|
299
|
+
subscription_id: str,
|
|
300
|
+
resource_group: str,
|
|
301
|
+
locations: List[str],
|
|
302
|
+
create_default_network: bool,
|
|
303
|
+
):
|
|
304
|
+
def func(location: str):
|
|
305
|
+
network_manager = NetworkManager(
|
|
306
|
+
credential=credential, subscription_id=subscription_id
|
|
307
|
+
)
|
|
308
|
+
if create_default_network:
|
|
309
|
+
network_manager.create_virtual_network(
|
|
310
|
+
resource_group=resource_group,
|
|
311
|
+
location=location,
|
|
312
|
+
name=azure_utils.get_default_network_name(resource_group, location),
|
|
313
|
+
subnet_name=azure_utils.get_default_subnet_name(resource_group, location),
|
|
314
|
+
)
|
|
315
|
+
network_manager.create_network_security_group(
|
|
316
|
+
resource_group=resource_group,
|
|
317
|
+
location=location,
|
|
318
|
+
name=azure_utils.get_default_network_security_group_name(resource_group, location),
|
|
319
|
+
)
|
|
320
|
+
network_manager.create_gateway_network_security_group(
|
|
321
|
+
resource_group=resource_group,
|
|
322
|
+
location=location,
|
|
323
|
+
name=azure_utils.get_gateway_network_security_group_name(resource_group, location),
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
with ThreadPoolExecutor(max_workers=8) as executor:
|
|
327
|
+
for location in locations:
|
|
328
|
+
executor.submit(func, location)
|
|
329
|
+
|
|
399
330
|
|
|
400
331
|
def _get_resource_group_name(project_name: str) -> str:
|
|
401
332
|
return f"dstack-{project_name}"
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
from typing import Annotated, Dict, List, Literal, Optional, Union
|
|
2
|
+
|
|
3
|
+
from pydantic import Field
|
|
4
|
+
|
|
5
|
+
from dstack._internal.core.models.common import CoreModel
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class AzureClientCreds(CoreModel):
|
|
9
|
+
type: Annotated[Literal["client"], Field(description="The type of credentials")] = "client"
|
|
10
|
+
client_id: Annotated[str, Field(description="The client ID")]
|
|
11
|
+
client_secret: Annotated[str, Field(description="The client secret")]
|
|
12
|
+
# if tenant_id is missing, it will be populated from config info
|
|
13
|
+
tenant_id: Optional[str]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class AzureDefaultCreds(CoreModel):
|
|
17
|
+
type: Annotated[Literal["default"], Field(description="The type of credentials")] = "default"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
AnyAzureCreds = Union[AzureClientCreds, AzureDefaultCreds]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class AzureCreds(CoreModel):
|
|
24
|
+
__root__: AnyAzureCreds = Field(..., discriminator="type")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class AzureBackendConfig(CoreModel):
|
|
28
|
+
type: Annotated[Literal["azure"], Field(description="The type of the backend")] = "azure"
|
|
29
|
+
tenant_id: Annotated[str, Field(description="The tenant ID")]
|
|
30
|
+
subscription_id: Annotated[str, Field(description="The subscription ID")]
|
|
31
|
+
resource_group: Annotated[
|
|
32
|
+
Optional[str],
|
|
33
|
+
Field(
|
|
34
|
+
description=(
|
|
35
|
+
"The resource group for resources created by `dstack`."
|
|
36
|
+
" If not specified, `dstack` will create a new resource group"
|
|
37
|
+
)
|
|
38
|
+
),
|
|
39
|
+
] = None
|
|
40
|
+
regions: Annotated[
|
|
41
|
+
Optional[List[str]],
|
|
42
|
+
Field(description="The list of Azure regions (locations). Omit to use all regions"),
|
|
43
|
+
] = None
|
|
44
|
+
vpc_ids: Annotated[
|
|
45
|
+
Optional[Dict[str, str]],
|
|
46
|
+
Field(
|
|
47
|
+
description=(
|
|
48
|
+
"The mapping from configured Azure locations to network IDs."
|
|
49
|
+
" A network ID must have a format `networkResourceGroup/networkName`"
|
|
50
|
+
" If not specified, `dstack` will create a new network for every configured region"
|
|
51
|
+
)
|
|
52
|
+
),
|
|
53
|
+
] = None
|
|
54
|
+
public_ips: Annotated[
|
|
55
|
+
Optional[bool],
|
|
56
|
+
Field(
|
|
57
|
+
description=(
|
|
58
|
+
"A flag to enable/disable public IP assigning on instances."
|
|
59
|
+
" `public_ips: false` requires `vpc_ids` that specifies custom networks with outbound internet connectivity"
|
|
60
|
+
" provided by NAT Gateway or other mechanism."
|
|
61
|
+
" Defaults to `true`"
|
|
62
|
+
)
|
|
63
|
+
),
|
|
64
|
+
] = None
|
|
65
|
+
tags: Annotated[
|
|
66
|
+
Optional[Dict[str, str]],
|
|
67
|
+
Field(description="The tags that will be assigned to resources created by `dstack`"),
|
|
68
|
+
] = None
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class AzureBackendConfigWithCreds(AzureBackendConfig):
|
|
72
|
+
creds: AnyAzureCreds = Field(..., description="The credentials", discriminator="type")
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
AnyAzureBackendConfig = Union[AzureBackendConfig, AzureBackendConfigWithCreds]
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class AzureStoredConfig(AzureBackendConfig):
|
|
79
|
+
resource_group: str = ""
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class AzureConfig(AzureStoredConfig):
|
|
83
|
+
creds: AnyAzureCreds
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
def allocate_public_ips(self) -> bool:
|
|
87
|
+
if self.public_ips is not None:
|
|
88
|
+
return self.public_ips
|
|
89
|
+
return True
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
from abc import ABC, abstractmethod
|
|
2
|
-
|
|
3
|
-
from dstack._internal.core.backends.base.compute import Compute
|
|
4
|
-
from dstack._internal.core.models.backends.base import BackendType
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
class Backend(ABC):
|
|
8
|
-
TYPE: BackendType
|
|
9
|
-
|
|
10
|
-
@abstractmethod
|
|
11
|
-
def compute(self) -> Compute:
|
|
12
|
-
pass
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from typing import ClassVar
|
|
3
|
+
|
|
4
|
+
from dstack._internal.core.backends.base.compute import Compute
|
|
5
|
+
from dstack._internal.core.models.backends.base import BackendType
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Backend(ABC):
|
|
9
|
+
TYPE: ClassVar[BackendType]
|
|
10
|
+
# `COMPUTE_CLASS` is used to introspect compute features without initializing it.
|
|
11
|
+
COMPUTE_CLASS: ClassVar[type[Compute]]
|
|
12
|
+
|
|
13
|
+
@abstractmethod
|
|
14
|
+
def compute(self) -> Compute:
|
|
15
|
+
"""
|
|
16
|
+
Returns Compute instance.
|
|
17
|
+
"""
|
|
18
|
+
pass
|