dstack 0.18.40rc1__py3-none-any.whl → 0.18.41__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/apply.py +8 -5
- dstack/_internal/cli/services/configurators/base.py +4 -2
- dstack/_internal/cli/services/configurators/fleet.py +21 -9
- dstack/_internal/cli/services/configurators/gateway.py +15 -0
- dstack/_internal/cli/services/configurators/run.py +6 -5
- dstack/_internal/cli/services/configurators/volume.py +15 -0
- dstack/_internal/cli/services/repos.py +3 -3
- dstack/_internal/cli/utils/fleet.py +44 -33
- dstack/_internal/cli/utils/run.py +27 -7
- dstack/_internal/cli/utils/volume.py +21 -9
- dstack/_internal/core/backends/aws/compute.py +92 -52
- dstack/_internal/core/backends/aws/resources.py +22 -12
- dstack/_internal/core/backends/azure/compute.py +2 -0
- dstack/_internal/core/backends/base/compute.py +20 -2
- dstack/_internal/core/backends/gcp/compute.py +30 -23
- dstack/_internal/core/backends/gcp/resources.py +0 -15
- dstack/_internal/core/backends/oci/compute.py +10 -5
- dstack/_internal/core/backends/oci/resources.py +23 -26
- dstack/_internal/core/backends/remote/provisioning.py +65 -27
- dstack/_internal/core/backends/runpod/compute.py +1 -0
- dstack/_internal/core/models/backends/azure.py +3 -1
- dstack/_internal/core/models/configurations.py +24 -1
- dstack/_internal/core/models/fleets.py +46 -0
- dstack/_internal/core/models/instances.py +5 -1
- dstack/_internal/core/models/pools.py +4 -1
- dstack/_internal/core/models/profiles.py +10 -4
- dstack/_internal/core/models/runs.py +20 -0
- dstack/_internal/core/models/volumes.py +3 -0
- dstack/_internal/core/services/ssh/attach.py +92 -53
- dstack/_internal/core/services/ssh/tunnel.py +58 -31
- dstack/_internal/proxy/gateway/routers/registry.py +2 -0
- dstack/_internal/proxy/gateway/schemas/registry.py +2 -0
- dstack/_internal/proxy/gateway/services/registry.py +4 -0
- dstack/_internal/proxy/lib/models.py +3 -0
- dstack/_internal/proxy/lib/services/service_connection.py +8 -1
- dstack/_internal/server/background/tasks/process_instances.py +72 -33
- dstack/_internal/server/background/tasks/process_metrics.py +9 -9
- dstack/_internal/server/background/tasks/process_running_jobs.py +73 -26
- dstack/_internal/server/background/tasks/process_runs.py +2 -12
- dstack/_internal/server/background/tasks/process_submitted_jobs.py +109 -42
- dstack/_internal/server/background/tasks/process_terminating_jobs.py +1 -1
- dstack/_internal/server/migrations/versions/1338b788b612_reverse_job_instance_relationship.py +71 -0
- dstack/_internal/server/migrations/versions/1e76fb0dde87_add_jobmodel_inactivity_secs.py +32 -0
- dstack/_internal/server/migrations/versions/51d45659d574_add_instancemodel_blocks_fields.py +43 -0
- dstack/_internal/server/migrations/versions/63c3f19cb184_add_jobterminationreason_inactivity_.py +83 -0
- dstack/_internal/server/models.py +10 -4
- dstack/_internal/server/routers/runs.py +1 -0
- dstack/_internal/server/schemas/runner.py +1 -0
- dstack/_internal/server/services/backends/configurators/azure.py +34 -8
- dstack/_internal/server/services/config.py +9 -0
- dstack/_internal/server/services/fleets.py +27 -2
- dstack/_internal/server/services/gateways/client.py +9 -1
- dstack/_internal/server/services/jobs/__init__.py +215 -43
- dstack/_internal/server/services/jobs/configurators/base.py +47 -2
- dstack/_internal/server/services/offers.py +91 -5
- dstack/_internal/server/services/pools.py +95 -11
- dstack/_internal/server/services/proxy/repo.py +17 -3
- dstack/_internal/server/services/runner/client.py +1 -1
- dstack/_internal/server/services/runner/ssh.py +33 -5
- dstack/_internal/server/services/runs.py +48 -179
- dstack/_internal/server/services/services/__init__.py +9 -1
- dstack/_internal/server/statics/index.html +1 -1
- dstack/_internal/server/statics/{main-11ec5e4a00ea6ec833e3.js → main-2ac66bfcbd2e39830b88.js} +30 -31
- dstack/_internal/server/statics/{main-11ec5e4a00ea6ec833e3.js.map → main-2ac66bfcbd2e39830b88.js.map} +1 -1
- dstack/_internal/server/statics/{main-fc56d1f4af8e57522a1c.css → main-ad5150a441de98cd8987.css} +1 -1
- dstack/_internal/server/testing/common.py +117 -52
- dstack/_internal/utils/common.py +22 -8
- dstack/_internal/utils/env.py +14 -0
- dstack/_internal/utils/ssh.py +1 -1
- dstack/api/server/_fleets.py +25 -1
- dstack/api/server/_runs.py +23 -2
- dstack/api/server/_volumes.py +12 -1
- dstack/version.py +1 -1
- {dstack-0.18.40rc1.dist-info → dstack-0.18.41.dist-info}/METADATA +1 -1
- {dstack-0.18.40rc1.dist-info → dstack-0.18.41.dist-info}/RECORD +98 -89
- tests/_internal/cli/services/configurators/test_profile.py +3 -3
- tests/_internal/core/services/ssh/test_tunnel.py +56 -4
- tests/_internal/proxy/gateway/routers/test_registry.py +30 -7
- tests/_internal/server/background/tasks/test_process_instances.py +138 -20
- tests/_internal/server/background/tasks/test_process_metrics.py +12 -0
- tests/_internal/server/background/tasks/test_process_running_jobs.py +192 -0
- tests/_internal/server/background/tasks/test_process_runs.py +27 -3
- tests/_internal/server/background/tasks/test_process_submitted_jobs.py +48 -3
- tests/_internal/server/background/tasks/test_process_terminating_jobs.py +126 -13
- tests/_internal/server/routers/test_fleets.py +15 -2
- tests/_internal/server/routers/test_pools.py +6 -0
- tests/_internal/server/routers/test_runs.py +27 -0
- tests/_internal/server/services/jobs/__init__.py +0 -0
- tests/_internal/server/services/jobs/configurators/__init__.py +0 -0
- tests/_internal/server/services/jobs/configurators/test_base.py +72 -0
- tests/_internal/server/services/test_pools.py +4 -0
- tests/_internal/server/services/test_runs.py +5 -41
- tests/_internal/utils/test_common.py +21 -0
- tests/_internal/utils/test_env.py +38 -0
- {dstack-0.18.40rc1.dist-info → dstack-0.18.41.dist-info}/LICENSE.md +0 -0
- {dstack-0.18.40rc1.dist-info → dstack-0.18.41.dist-info}/WHEEL +0 -0
- {dstack-0.18.40rc1.dist-info → dstack-0.18.41.dist-info}/entry_points.txt +0 -0
- {dstack-0.18.40rc1.dist-info → dstack-0.18.41.dist-info}/top_level.txt +0 -0
|
@@ -29,7 +29,6 @@ from dstack._internal.core.models.runs import (
|
|
|
29
29
|
ApplyRunPlanInput,
|
|
30
30
|
Job,
|
|
31
31
|
JobPlan,
|
|
32
|
-
JobProvisioningData,
|
|
33
32
|
JobSpec,
|
|
34
33
|
JobStatus,
|
|
35
34
|
JobSubmission,
|
|
@@ -45,8 +44,6 @@ from dstack._internal.core.models.users import GlobalRole
|
|
|
45
44
|
from dstack._internal.core.models.volumes import (
|
|
46
45
|
InstanceMountPoint,
|
|
47
46
|
Volume,
|
|
48
|
-
VolumeMountPoint,
|
|
49
|
-
VolumeStatus,
|
|
50
47
|
)
|
|
51
48
|
from dstack._internal.core.services import validate_dstack_resource_name
|
|
52
49
|
from dstack._internal.core.services.diff import diff_models
|
|
@@ -58,15 +55,15 @@ from dstack._internal.server.models import (
|
|
|
58
55
|
RepoModel,
|
|
59
56
|
RunModel,
|
|
60
57
|
UserModel,
|
|
61
|
-
VolumeModel,
|
|
62
58
|
)
|
|
63
59
|
from dstack._internal.server.services import repos as repos_services
|
|
64
60
|
from dstack._internal.server.services import services
|
|
65
|
-
from dstack._internal.server.services import volumes as volumes_services
|
|
66
61
|
from dstack._internal.server.services.docker import is_valid_docker_volume_target
|
|
67
62
|
from dstack._internal.server.services.jobs import (
|
|
63
|
+
check_can_attach_job_volumes,
|
|
68
64
|
delay_job_instance_termination,
|
|
69
65
|
get_instances_ids_with_detaching_volumes,
|
|
66
|
+
get_job_configured_volumes,
|
|
70
67
|
get_jobs_from_run_spec,
|
|
71
68
|
group_jobs_by_replica_latest,
|
|
72
69
|
job_model_to_job_submission,
|
|
@@ -80,6 +77,7 @@ from dstack._internal.server.services.pools import (
|
|
|
80
77
|
get_instance_offer,
|
|
81
78
|
get_or_create_pool_by_name,
|
|
82
79
|
get_pool_instances,
|
|
80
|
+
get_shared_pool_instances_with_offers,
|
|
83
81
|
)
|
|
84
82
|
from dstack._internal.server.services.projects import list_project_models, list_user_project_models
|
|
85
83
|
from dstack._internal.server.services.users import get_user_model_by_name
|
|
@@ -164,7 +162,8 @@ async def list_projects_run_models(
|
|
|
164
162
|
limit: int,
|
|
165
163
|
ascending: bool,
|
|
166
164
|
) -> List[RunModel]:
|
|
167
|
-
filters = [
|
|
165
|
+
filters = []
|
|
166
|
+
filters.append(RunModel.project_id.in_(p.id for p in projects))
|
|
168
167
|
if repo is not None:
|
|
169
168
|
filters.append(RunModel.repo_id == repo.id)
|
|
170
169
|
if runs_user is not None:
|
|
@@ -300,10 +299,11 @@ async def get_plan(
|
|
|
300
299
|
# TODO(egor-s): do we need to generate all replicas here?
|
|
301
300
|
jobs = await get_jobs_from_run_spec(run_spec, replica_num=0)
|
|
302
301
|
|
|
303
|
-
volumes = await
|
|
302
|
+
volumes = await get_job_configured_volumes(
|
|
304
303
|
session=session,
|
|
305
304
|
project=project,
|
|
306
305
|
run_spec=run_spec,
|
|
306
|
+
job_num=0,
|
|
307
307
|
)
|
|
308
308
|
|
|
309
309
|
pool = await get_or_create_pool_by_name(
|
|
@@ -450,7 +450,7 @@ async def submit_run(
|
|
|
450
450
|
else:
|
|
451
451
|
await delete_runs(session=session, project=project, runs_names=[run_spec.run_name])
|
|
452
452
|
|
|
453
|
-
await
|
|
453
|
+
await _validate_run(
|
|
454
454
|
session=session,
|
|
455
455
|
user=user,
|
|
456
456
|
project=project,
|
|
@@ -677,27 +677,40 @@ async def _get_pool_offers(
|
|
|
677
677
|
run_spec: RunSpec,
|
|
678
678
|
job: Job,
|
|
679
679
|
volumes: List[List[Volume]],
|
|
680
|
-
) ->
|
|
680
|
+
) -> list[InstanceOfferWithAvailability]:
|
|
681
|
+
pool_offers: list[InstanceOfferWithAvailability] = []
|
|
682
|
+
|
|
681
683
|
detaching_instances_ids = await get_instances_ids_with_detaching_volumes(session)
|
|
682
684
|
pool_instances = [i for i in get_pool_instances(pool) if i.id not in detaching_instances_ids]
|
|
683
|
-
|
|
685
|
+
multinode = job.job_spec.jobs_per_replica > 1
|
|
686
|
+
|
|
687
|
+
if not multinode:
|
|
688
|
+
shared_instances_with_offers = get_shared_pool_instances_with_offers(
|
|
689
|
+
pool_instances=pool_instances,
|
|
690
|
+
profile=run_spec.merged_profile,
|
|
691
|
+
requirements=job.job_spec.requirements,
|
|
692
|
+
volumes=volumes,
|
|
693
|
+
)
|
|
694
|
+
for _, offer in shared_instances_with_offers:
|
|
695
|
+
pool_offers.append(offer)
|
|
696
|
+
|
|
697
|
+
nonshared_instances = filter_pool_instances(
|
|
684
698
|
pool_instances=pool_instances,
|
|
685
699
|
profile=run_spec.merged_profile,
|
|
686
700
|
requirements=job.job_spec.requirements,
|
|
687
|
-
multinode=
|
|
701
|
+
multinode=multinode,
|
|
688
702
|
volumes=volumes,
|
|
703
|
+
shared=False,
|
|
689
704
|
)
|
|
690
|
-
|
|
691
|
-
for instance in pool_filtered_instances:
|
|
705
|
+
for instance in nonshared_instances:
|
|
692
706
|
offer = get_instance_offer(instance)
|
|
693
707
|
if offer is None:
|
|
694
708
|
continue
|
|
695
709
|
offer.availability = InstanceAvailability.BUSY
|
|
696
710
|
if instance.status == InstanceStatus.IDLE:
|
|
697
711
|
offer.availability = InstanceAvailability.IDLE
|
|
698
|
-
if instance.unreachable:
|
|
699
|
-
offer.availability = InstanceAvailability.NOT_AVAILABLE
|
|
700
712
|
pool_offers.append(offer)
|
|
713
|
+
|
|
701
714
|
pool_offers.sort(key=lambda offer: offer.price)
|
|
702
715
|
return pool_offers
|
|
703
716
|
|
|
@@ -722,186 +735,42 @@ async def _generate_run_name(
|
|
|
722
735
|
idx += 1
|
|
723
736
|
|
|
724
737
|
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
run_spec: RunSpec,
|
|
730
|
-
):
|
|
731
|
-
volumes = await get_run_volumes(
|
|
732
|
-
session=session,
|
|
733
|
-
project=project,
|
|
734
|
-
run_spec=run_spec,
|
|
735
|
-
)
|
|
736
|
-
check_can_attach_run_volumes(
|
|
737
|
-
run_spec=run_spec,
|
|
738
|
-
volumes=volumes,
|
|
738
|
+
def check_run_spec_requires_instance_mounts(run_spec: RunSpec) -> bool:
|
|
739
|
+
return any(
|
|
740
|
+
is_core_model_instance(mp, InstanceMountPoint) and not mp.optional
|
|
741
|
+
for mp in run_spec.configuration.volumes
|
|
739
742
|
)
|
|
740
743
|
|
|
741
744
|
|
|
742
|
-
async def
|
|
745
|
+
async def _validate_run(
|
|
743
746
|
session: AsyncSession,
|
|
747
|
+
user: UserModel,
|
|
744
748
|
project: ProjectModel,
|
|
745
749
|
run_spec: RunSpec,
|
|
746
|
-
)
|
|
747
|
-
|
|
748
|
-
Returns list of run volumes grouped by mount points.
|
|
749
|
-
"""
|
|
750
|
-
volume_models = await get_run_volume_models(
|
|
750
|
+
):
|
|
751
|
+
await _validate_run_volumes(
|
|
751
752
|
session=session,
|
|
752
753
|
project=project,
|
|
753
754
|
run_spec=run_spec,
|
|
754
755
|
)
|
|
755
|
-
return [
|
|
756
|
-
[volumes_services.volume_model_to_volume(v) for v in mount_point_volume_models]
|
|
757
|
-
for mount_point_volume_models in volume_models
|
|
758
|
-
]
|
|
759
756
|
|
|
760
757
|
|
|
761
|
-
async def
|
|
758
|
+
async def _validate_run_volumes(
|
|
762
759
|
session: AsyncSession,
|
|
763
760
|
project: ProjectModel,
|
|
764
761
|
run_spec: RunSpec,
|
|
765
|
-
) -> List[List[VolumeModel]]:
|
|
766
|
-
"""
|
|
767
|
-
Returns list of run volume models grouped by mount points.
|
|
768
|
-
"""
|
|
769
|
-
if len(run_spec.configuration.volumes) == 0:
|
|
770
|
-
return []
|
|
771
|
-
volume_models = []
|
|
772
|
-
for mount_point in run_spec.configuration.volumes:
|
|
773
|
-
if not is_core_model_instance(mount_point, VolumeMountPoint):
|
|
774
|
-
continue
|
|
775
|
-
if isinstance(mount_point.name, str):
|
|
776
|
-
names = [mount_point.name]
|
|
777
|
-
else:
|
|
778
|
-
names = mount_point.name
|
|
779
|
-
mount_point_volume_models = []
|
|
780
|
-
for name in names:
|
|
781
|
-
volume_model = await volumes_services.get_project_volume_model_by_name(
|
|
782
|
-
session=session,
|
|
783
|
-
project=project,
|
|
784
|
-
name=name,
|
|
785
|
-
)
|
|
786
|
-
if volume_model is None:
|
|
787
|
-
raise ResourceNotExistsError(f"Volume {mount_point.name} not found")
|
|
788
|
-
mount_point_volume_models.append(volume_model)
|
|
789
|
-
volume_models.append(mount_point_volume_models)
|
|
790
|
-
return volume_models
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
def check_can_attach_run_volumes(
|
|
794
|
-
run_spec: RunSpec,
|
|
795
|
-
volumes: List[List[Volume]],
|
|
796
762
|
):
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
""
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
regions = {v.configuration.region for v in mount_point_volumes}
|
|
809
|
-
if backends != expected_backends:
|
|
810
|
-
raise ServerClientError(
|
|
811
|
-
"Volumes from different backends specified for different mount points"
|
|
812
|
-
)
|
|
813
|
-
if regions != expected_regions:
|
|
814
|
-
raise ServerClientError(
|
|
815
|
-
"Volumes from different regions specified for different mount points"
|
|
816
|
-
)
|
|
817
|
-
for volume in mount_point_volumes:
|
|
818
|
-
if volume.status != VolumeStatus.ACTIVE:
|
|
819
|
-
raise ServerClientError(f"Cannot mount volumes that are not active: {volume.name}")
|
|
820
|
-
volumes_names = [v.name for vs in volumes for v in vs]
|
|
821
|
-
if len(volumes_names) != len(set(volumes_names)):
|
|
822
|
-
raise ServerClientError("Cannot attach the same volume at different mount points")
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
async def get_job_volumes(
|
|
826
|
-
session: AsyncSession,
|
|
827
|
-
project: ProjectModel,
|
|
828
|
-
run_spec: RunSpec,
|
|
829
|
-
job_provisioning_data: JobProvisioningData,
|
|
830
|
-
) -> List[Volume]:
|
|
831
|
-
"""
|
|
832
|
-
Returns volumes attached to the job.
|
|
833
|
-
"""
|
|
834
|
-
run_volumes = await get_run_volumes(
|
|
835
|
-
session=session,
|
|
836
|
-
project=project,
|
|
837
|
-
run_spec=run_spec,
|
|
838
|
-
)
|
|
839
|
-
job_volumes = []
|
|
840
|
-
for mount_point_volumes in run_volumes:
|
|
841
|
-
job_volumes.append(get_job_mount_point_volume(mount_point_volumes, job_provisioning_data))
|
|
842
|
-
return job_volumes
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
def get_job_mount_point_volume(
|
|
846
|
-
volumes: List[Volume],
|
|
847
|
-
job_provisioning_data: JobProvisioningData,
|
|
848
|
-
) -> Volume:
|
|
849
|
-
"""
|
|
850
|
-
Returns the volume attached to the job among the list of possible mount point volumes.
|
|
851
|
-
"""
|
|
852
|
-
for volume in volumes:
|
|
853
|
-
if (
|
|
854
|
-
volume.configuration.backend != job_provisioning_data.get_base_backend()
|
|
855
|
-
or volume.configuration.region != job_provisioning_data.region
|
|
856
|
-
):
|
|
857
|
-
continue
|
|
858
|
-
if (
|
|
859
|
-
volume.provisioning_data is not None
|
|
860
|
-
and volume.provisioning_data.availability_zone is not None
|
|
861
|
-
and job_provisioning_data.availability_zone is not None
|
|
862
|
-
and volume.provisioning_data.availability_zone
|
|
863
|
-
!= job_provisioning_data.availability_zone
|
|
864
|
-
):
|
|
865
|
-
continue
|
|
866
|
-
return volume
|
|
867
|
-
raise ServerClientError("Failed to find an eligible volume for the mount point")
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
def get_offer_volumes(
|
|
871
|
-
volumes: List[List[Volume]],
|
|
872
|
-
offer: InstanceOfferWithAvailability,
|
|
873
|
-
) -> List[Volume]:
|
|
874
|
-
"""
|
|
875
|
-
Returns volumes suitable for the offer for each mount point.
|
|
876
|
-
"""
|
|
877
|
-
offer_volumes = []
|
|
878
|
-
for mount_point_volumes in volumes:
|
|
879
|
-
offer_volumes.append(get_offer_mount_point_volume(mount_point_volumes, offer))
|
|
880
|
-
return offer_volumes
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
def get_offer_mount_point_volume(
|
|
884
|
-
volumes: List[Volume],
|
|
885
|
-
offer: InstanceOfferWithAvailability,
|
|
886
|
-
) -> Volume:
|
|
887
|
-
"""
|
|
888
|
-
Returns the first suitable volume for the offer among possible mount point volumes.
|
|
889
|
-
"""
|
|
890
|
-
for volume in volumes:
|
|
891
|
-
if (
|
|
892
|
-
volume.configuration.backend != offer.backend
|
|
893
|
-
or volume.configuration.region != offer.region
|
|
894
|
-
):
|
|
895
|
-
continue
|
|
896
|
-
return volume
|
|
897
|
-
raise ServerClientError("Failed to find an eligible volume for the mount point")
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
def check_run_spec_requires_instance_mounts(run_spec: RunSpec) -> bool:
|
|
901
|
-
return any(
|
|
902
|
-
is_core_model_instance(mp, InstanceMountPoint) and not mp.optional
|
|
903
|
-
for mp in run_spec.configuration.volumes
|
|
904
|
-
)
|
|
763
|
+
# The volumes validation should be done here and not in job configurator
|
|
764
|
+
# since potentially we may need to validate volumes for jobs/replicas
|
|
765
|
+
# that won't be created immediately (e.g. range of replicas or nodes).
|
|
766
|
+
nodes = 1
|
|
767
|
+
if run_spec.configuration.type == "task":
|
|
768
|
+
nodes = run_spec.configuration.nodes
|
|
769
|
+
for job_num in range(nodes):
|
|
770
|
+
volumes = await get_job_configured_volumes(
|
|
771
|
+
session=session, project=project, run_spec=run_spec, job_num=job_num
|
|
772
|
+
)
|
|
773
|
+
check_can_attach_job_volumes(volumes=volumes)
|
|
905
774
|
|
|
906
775
|
|
|
907
776
|
async def _get_run_repo_or_error(
|
|
@@ -21,6 +21,7 @@ from dstack._internal.core.errors import (
|
|
|
21
21
|
from dstack._internal.core.models.common import is_core_model_instance
|
|
22
22
|
from dstack._internal.core.models.configurations import SERVICE_HTTPS_DEFAULT, ServiceConfiguration
|
|
23
23
|
from dstack._internal.core.models.gateways import GatewayConfiguration, GatewayStatus
|
|
24
|
+
from dstack._internal.core.models.instances import SSHConnectionParams
|
|
24
25
|
from dstack._internal.core.models.runs import Run, RunSpec, ServiceModelSpec, ServiceSpec
|
|
25
26
|
from dstack._internal.server import settings
|
|
26
27
|
from dstack._internal.server.models import GatewayModel, JobModel, ProjectModel, RunModel
|
|
@@ -155,7 +156,12 @@ def get_service_spec(
|
|
|
155
156
|
|
|
156
157
|
|
|
157
158
|
async def register_replica(
|
|
158
|
-
session: AsyncSession,
|
|
159
|
+
session: AsyncSession,
|
|
160
|
+
gateway_id: Optional[uuid.UUID],
|
|
161
|
+
run: Run,
|
|
162
|
+
job_model: JobModel,
|
|
163
|
+
ssh_head_proxy: Optional[SSHConnectionParams],
|
|
164
|
+
ssh_head_proxy_private_key: Optional[str],
|
|
159
165
|
):
|
|
160
166
|
if gateway_id is None: # in-server proxy
|
|
161
167
|
return
|
|
@@ -167,6 +173,8 @@ async def register_replica(
|
|
|
167
173
|
await client.register_replica(
|
|
168
174
|
run=run,
|
|
169
175
|
job_submission=job_submission,
|
|
176
|
+
ssh_head_proxy=ssh_head_proxy,
|
|
177
|
+
ssh_head_proxy_private_key=ssh_head_proxy_private_key,
|
|
170
178
|
)
|
|
171
179
|
logger.info("%s: replica is registered for service %s", fmt(job_model), run.id.hex)
|
|
172
180
|
except (httpx.RequestError, SSHError) as e:
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
<!doctype html><html lang="en"><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><title>dstack</title><meta name="description" content="Get GPUs at the best prices and availability from a wide range of providers. No cloud account of your own is required.
|
|
2
2
|
"/><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap" rel="stylesheet"><meta name="og:title" content="dstack"><meta name="og:type" content="article"><meta name="og:image" content="/splash_thumbnail.png"><meta name="og:description" content="Get GPUs at the best prices and availability from a wide range of providers. No cloud account of your own is required.
|
|
3
|
-
"><link rel="icon" type="image/x-icon" href="/assets/favicon.ico"><link rel="icon" type="image/png" sizes="16x16" href="/assets/favicon-16x16.png"><link rel="icon" type="image/png" sizes="32x32" href="/assets/favicon-32x32.png"><link rel="icon" type="image/png" sizes="48x48" href="/assets/favicon-48x48.png"><link rel="manifest" href="/assets/manifest.webmanifest"><meta name="mobile-web-app-capable" content="yes"><meta name="theme-color" content="#fff"><meta name="application-name" content="dstackai"><link rel="apple-touch-icon" sizes="57x57" href="/assets/apple-touch-icon-57x57.png"><link rel="apple-touch-icon" sizes="60x60" href="/assets/apple-touch-icon-60x60.png"><link rel="apple-touch-icon" sizes="72x72" href="/assets/apple-touch-icon-72x72.png"><link rel="apple-touch-icon" sizes="76x76" href="/assets/apple-touch-icon-76x76.png"><link rel="apple-touch-icon" sizes="114x114" href="/assets/apple-touch-icon-114x114.png"><link rel="apple-touch-icon" sizes="120x120" href="/assets/apple-touch-icon-120x120.png"><link rel="apple-touch-icon" sizes="144x144" href="/assets/apple-touch-icon-144x144.png"><link rel="apple-touch-icon" sizes="152x152" href="/assets/apple-touch-icon-152x152.png"><link rel="apple-touch-icon" sizes="167x167" href="/assets/apple-touch-icon-167x167.png"><link rel="apple-touch-icon" sizes="180x180" href="/assets/apple-touch-icon-180x180.png"><link rel="apple-touch-icon" sizes="1024x1024" href="/assets/apple-touch-icon-1024x1024.png"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"><meta name="apple-mobile-web-app-title" content="dstackai"><link rel="apple-touch-startup-image" media="(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/assets/apple-touch-startup-image-640x1136.png"><link rel="apple-touch-startup-image" media="(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="/assets/apple-touch-startup-image-1136x640.png"><link rel="apple-touch-startup-image" media="(device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/assets/apple-touch-startup-image-750x1334.png"><link rel="apple-touch-startup-image" media="(device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="/assets/apple-touch-startup-image-1334x750.png"><link rel="apple-touch-startup-image" media="(device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="/assets/apple-touch-startup-image-1125x2436.png"><link rel="apple-touch-startup-image" media="(device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2436x1125.png"><link rel="apple-touch-startup-image" media="(device-width: 390px) and (device-height: 844px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="/assets/apple-touch-startup-image-1170x2532.png"><link rel="apple-touch-startup-image" media="(device-width: 390px) and (device-height: 844px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2532x1170.png"><link rel="apple-touch-startup-image" media="(device-width: 393px) and (device-height: 852px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="/assets/apple-touch-startup-image-1179x2556.png"><link rel="apple-touch-startup-image" media="(device-width: 393px) and (device-height: 852px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2556x1179.png"><link rel="apple-touch-startup-image" media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/assets/apple-touch-startup-image-828x1792.png"><link rel="apple-touch-startup-image" media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="/assets/apple-touch-startup-image-1792x828.png"><link rel="apple-touch-startup-image" media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="/assets/apple-touch-startup-image-1242x2688.png"><link rel="apple-touch-startup-image" media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2688x1242.png"><link rel="apple-touch-startup-image" media="(device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="/assets/apple-touch-startup-image-1242x2208.png"><link rel="apple-touch-startup-image" media="(device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2208x1242.png"><link rel="apple-touch-startup-image" media="(device-width: 428px) and (device-height: 926px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="/assets/apple-touch-startup-image-1284x2778.png"><link rel="apple-touch-startup-image" media="(device-width: 428px) and (device-height: 926px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2778x1284.png"><link rel="apple-touch-startup-image" media="(device-width: 430px) and (device-height: 932px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="/assets/apple-touch-startup-image-1290x2796.png"><link rel="apple-touch-startup-image" media="(device-width: 430px) and (device-height: 932px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2796x1290.png"><link rel="apple-touch-startup-image" media="(device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/assets/apple-touch-startup-image-1488x2266.png"><link rel="apple-touch-startup-image" media="(device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2266x1488.png"><link rel="apple-touch-startup-image" media="(device-width: 768px) and (device-height: 1024px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/assets/apple-touch-startup-image-1536x2048.png"><link rel="apple-touch-startup-image" media="(device-width: 768px) and (device-height: 1024px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2048x1536.png"><link rel="apple-touch-startup-image" media="(device-width: 810px) and (device-height: 1080px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/assets/apple-touch-startup-image-1620x2160.png"><link rel="apple-touch-startup-image" media="(device-width: 810px) and (device-height: 1080px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2160x1620.png"><link rel="apple-touch-startup-image" media="(device-width: 820px) and (device-height: 1080px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/assets/apple-touch-startup-image-1640x2160.png"><link rel="apple-touch-startup-image" media="(device-width: 820px) and (device-height: 1080px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2160x1640.png"><link rel="apple-touch-startup-image" media="(device-width: 834px) and (device-height: 1194px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/assets/apple-touch-startup-image-1668x2388.png"><link rel="apple-touch-startup-image" media="(device-width: 834px) and (device-height: 1194px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2388x1668.png"><link rel="apple-touch-startup-image" media="(device-width: 834px) and (device-height: 1112px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/assets/apple-touch-startup-image-1668x2224.png"><link rel="apple-touch-startup-image" media="(device-width: 834px) and (device-height: 1112px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2224x1668.png"><link rel="apple-touch-startup-image" media="(device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/assets/apple-touch-startup-image-2048x2732.png"><link rel="apple-touch-startup-image" media="(device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2732x2048.png"><meta name="msapplication-TileColor" content="#fff"><meta name="msapplication-TileImage" content="/assets/mstile-144x144.png"><meta name="msapplication-config" content="/assets/browserconfig.xml"><link rel="yandex-tableau-widget" href="/assets/yandex-browser-manifest.json"><script defer="defer" src="/main-
|
|
3
|
+
"><link rel="icon" type="image/x-icon" href="/assets/favicon.ico"><link rel="icon" type="image/png" sizes="16x16" href="/assets/favicon-16x16.png"><link rel="icon" type="image/png" sizes="32x32" href="/assets/favicon-32x32.png"><link rel="icon" type="image/png" sizes="48x48" href="/assets/favicon-48x48.png"><link rel="manifest" href="/assets/manifest.webmanifest"><meta name="mobile-web-app-capable" content="yes"><meta name="theme-color" content="#fff"><meta name="application-name" content="dstackai"><link rel="apple-touch-icon" sizes="57x57" href="/assets/apple-touch-icon-57x57.png"><link rel="apple-touch-icon" sizes="60x60" href="/assets/apple-touch-icon-60x60.png"><link rel="apple-touch-icon" sizes="72x72" href="/assets/apple-touch-icon-72x72.png"><link rel="apple-touch-icon" sizes="76x76" href="/assets/apple-touch-icon-76x76.png"><link rel="apple-touch-icon" sizes="114x114" href="/assets/apple-touch-icon-114x114.png"><link rel="apple-touch-icon" sizes="120x120" href="/assets/apple-touch-icon-120x120.png"><link rel="apple-touch-icon" sizes="144x144" href="/assets/apple-touch-icon-144x144.png"><link rel="apple-touch-icon" sizes="152x152" href="/assets/apple-touch-icon-152x152.png"><link rel="apple-touch-icon" sizes="167x167" href="/assets/apple-touch-icon-167x167.png"><link rel="apple-touch-icon" sizes="180x180" href="/assets/apple-touch-icon-180x180.png"><link rel="apple-touch-icon" sizes="1024x1024" href="/assets/apple-touch-icon-1024x1024.png"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"><meta name="apple-mobile-web-app-title" content="dstackai"><link rel="apple-touch-startup-image" media="(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/assets/apple-touch-startup-image-640x1136.png"><link rel="apple-touch-startup-image" media="(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="/assets/apple-touch-startup-image-1136x640.png"><link rel="apple-touch-startup-image" media="(device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/assets/apple-touch-startup-image-750x1334.png"><link rel="apple-touch-startup-image" media="(device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="/assets/apple-touch-startup-image-1334x750.png"><link rel="apple-touch-startup-image" media="(device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="/assets/apple-touch-startup-image-1125x2436.png"><link rel="apple-touch-startup-image" media="(device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2436x1125.png"><link rel="apple-touch-startup-image" media="(device-width: 390px) and (device-height: 844px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="/assets/apple-touch-startup-image-1170x2532.png"><link rel="apple-touch-startup-image" media="(device-width: 390px) and (device-height: 844px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2532x1170.png"><link rel="apple-touch-startup-image" media="(device-width: 393px) and (device-height: 852px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="/assets/apple-touch-startup-image-1179x2556.png"><link rel="apple-touch-startup-image" media="(device-width: 393px) and (device-height: 852px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2556x1179.png"><link rel="apple-touch-startup-image" media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/assets/apple-touch-startup-image-828x1792.png"><link rel="apple-touch-startup-image" media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="/assets/apple-touch-startup-image-1792x828.png"><link rel="apple-touch-startup-image" media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="/assets/apple-touch-startup-image-1242x2688.png"><link rel="apple-touch-startup-image" media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2688x1242.png"><link rel="apple-touch-startup-image" media="(device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="/assets/apple-touch-startup-image-1242x2208.png"><link rel="apple-touch-startup-image" media="(device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2208x1242.png"><link rel="apple-touch-startup-image" media="(device-width: 428px) and (device-height: 926px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="/assets/apple-touch-startup-image-1284x2778.png"><link rel="apple-touch-startup-image" media="(device-width: 428px) and (device-height: 926px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2778x1284.png"><link rel="apple-touch-startup-image" media="(device-width: 430px) and (device-height: 932px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="/assets/apple-touch-startup-image-1290x2796.png"><link rel="apple-touch-startup-image" media="(device-width: 430px) and (device-height: 932px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2796x1290.png"><link rel="apple-touch-startup-image" media="(device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/assets/apple-touch-startup-image-1488x2266.png"><link rel="apple-touch-startup-image" media="(device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2266x1488.png"><link rel="apple-touch-startup-image" media="(device-width: 768px) and (device-height: 1024px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/assets/apple-touch-startup-image-1536x2048.png"><link rel="apple-touch-startup-image" media="(device-width: 768px) and (device-height: 1024px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2048x1536.png"><link rel="apple-touch-startup-image" media="(device-width: 810px) and (device-height: 1080px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/assets/apple-touch-startup-image-1620x2160.png"><link rel="apple-touch-startup-image" media="(device-width: 810px) and (device-height: 1080px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2160x1620.png"><link rel="apple-touch-startup-image" media="(device-width: 820px) and (device-height: 1080px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/assets/apple-touch-startup-image-1640x2160.png"><link rel="apple-touch-startup-image" media="(device-width: 820px) and (device-height: 1080px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2160x1640.png"><link rel="apple-touch-startup-image" media="(device-width: 834px) and (device-height: 1194px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/assets/apple-touch-startup-image-1668x2388.png"><link rel="apple-touch-startup-image" media="(device-width: 834px) and (device-height: 1194px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2388x1668.png"><link rel="apple-touch-startup-image" media="(device-width: 834px) and (device-height: 1112px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/assets/apple-touch-startup-image-1668x2224.png"><link rel="apple-touch-startup-image" media="(device-width: 834px) and (device-height: 1112px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2224x1668.png"><link rel="apple-touch-startup-image" media="(device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/assets/apple-touch-startup-image-2048x2732.png"><link rel="apple-touch-startup-image" media="(device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2732x2048.png"><meta name="msapplication-TileColor" content="#fff"><meta name="msapplication-TileImage" content="/assets/mstile-144x144.png"><meta name="msapplication-config" content="/assets/browserconfig.xml"><link rel="yandex-tableau-widget" href="/assets/yandex-browser-manifest.json"><script defer="defer" src="/main-2ac66bfcbd2e39830b88.js"></script><link href="/main-ad5150a441de98cd8987.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div class="b-page-header" id="header"></div><div id="root"></div></body></html>
|