skypilot-nightly 1.0.0.dev20250509__py3-none-any.whl → 1.0.0.dev20251107__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 skypilot-nightly might be problematic. Click here for more details.
- sky/__init__.py +22 -6
- sky/adaptors/aws.py +25 -7
- sky/adaptors/common.py +24 -1
- sky/adaptors/coreweave.py +278 -0
- sky/adaptors/do.py +8 -2
- sky/adaptors/hyperbolic.py +8 -0
- sky/adaptors/kubernetes.py +149 -18
- sky/adaptors/nebius.py +170 -17
- sky/adaptors/primeintellect.py +1 -0
- sky/adaptors/runpod.py +68 -0
- sky/adaptors/seeweb.py +167 -0
- sky/adaptors/shadeform.py +89 -0
- sky/admin_policy.py +187 -4
- sky/authentication.py +179 -225
- sky/backends/__init__.py +4 -2
- sky/backends/backend.py +22 -9
- sky/backends/backend_utils.py +1299 -380
- sky/backends/cloud_vm_ray_backend.py +1715 -518
- sky/backends/docker_utils.py +1 -1
- sky/backends/local_docker_backend.py +11 -6
- sky/backends/wheel_utils.py +37 -9
- sky/{clouds/service_catalog → catalog}/__init__.py +21 -19
- sky/{clouds/service_catalog → catalog}/aws_catalog.py +27 -8
- sky/{clouds/service_catalog → catalog}/azure_catalog.py +10 -7
- sky/{clouds/service_catalog → catalog}/common.py +89 -48
- sky/{clouds/service_catalog → catalog}/cudo_catalog.py +8 -5
- sky/{clouds/service_catalog → catalog}/data_fetchers/analyze.py +1 -1
- sky/{clouds/service_catalog → catalog}/data_fetchers/fetch_aws.py +30 -40
- sky/{clouds/service_catalog → catalog}/data_fetchers/fetch_cudo.py +38 -38
- sky/{clouds/service_catalog → catalog}/data_fetchers/fetch_gcp.py +42 -15
- sky/catalog/data_fetchers/fetch_hyperbolic.py +136 -0
- sky/{clouds/service_catalog → catalog}/data_fetchers/fetch_lambda_cloud.py +1 -0
- sky/catalog/data_fetchers/fetch_nebius.py +335 -0
- sky/catalog/data_fetchers/fetch_runpod.py +698 -0
- sky/catalog/data_fetchers/fetch_seeweb.py +329 -0
- sky/catalog/data_fetchers/fetch_shadeform.py +142 -0
- sky/{clouds/service_catalog → catalog}/data_fetchers/fetch_vast.py +1 -1
- sky/{clouds/service_catalog → catalog}/data_fetchers/fetch_vsphere.py +1 -1
- sky/{clouds/service_catalog → catalog}/do_catalog.py +5 -2
- sky/{clouds/service_catalog → catalog}/fluidstack_catalog.py +6 -3
- sky/{clouds/service_catalog → catalog}/gcp_catalog.py +41 -15
- sky/catalog/hyperbolic_catalog.py +136 -0
- sky/{clouds/service_catalog → catalog}/ibm_catalog.py +9 -6
- sky/{clouds/service_catalog → catalog}/kubernetes_catalog.py +36 -24
- sky/{clouds/service_catalog → catalog}/lambda_catalog.py +9 -6
- sky/{clouds/service_catalog → catalog}/nebius_catalog.py +9 -7
- sky/{clouds/service_catalog → catalog}/oci_catalog.py +9 -6
- sky/{clouds/service_catalog → catalog}/paperspace_catalog.py +5 -2
- sky/catalog/primeintellect_catalog.py +95 -0
- sky/{clouds/service_catalog → catalog}/runpod_catalog.py +11 -4
- sky/{clouds/service_catalog → catalog}/scp_catalog.py +9 -6
- sky/catalog/seeweb_catalog.py +184 -0
- sky/catalog/shadeform_catalog.py +165 -0
- sky/catalog/ssh_catalog.py +167 -0
- sky/{clouds/service_catalog → catalog}/vast_catalog.py +6 -3
- sky/{clouds/service_catalog → catalog}/vsphere_catalog.py +5 -2
- sky/check.py +491 -203
- sky/cli.py +5 -6005
- sky/client/{cli.py → cli/command.py} +2477 -1885
- sky/client/cli/deprecation_utils.py +99 -0
- sky/client/cli/flags.py +359 -0
- sky/client/cli/table_utils.py +320 -0
- sky/client/common.py +70 -32
- sky/client/oauth.py +82 -0
- sky/client/sdk.py +1203 -297
- sky/client/sdk_async.py +833 -0
- sky/client/service_account_auth.py +47 -0
- sky/cloud_stores.py +73 -0
- sky/clouds/__init__.py +13 -0
- sky/clouds/aws.py +358 -93
- sky/clouds/azure.py +105 -83
- sky/clouds/cloud.py +127 -36
- sky/clouds/cudo.py +68 -50
- sky/clouds/do.py +66 -48
- sky/clouds/fluidstack.py +63 -44
- sky/clouds/gcp.py +339 -110
- sky/clouds/hyperbolic.py +293 -0
- sky/clouds/ibm.py +70 -49
- sky/clouds/kubernetes.py +563 -162
- sky/clouds/lambda_cloud.py +74 -54
- sky/clouds/nebius.py +206 -80
- sky/clouds/oci.py +88 -66
- sky/clouds/paperspace.py +61 -44
- sky/clouds/primeintellect.py +317 -0
- sky/clouds/runpod.py +164 -74
- sky/clouds/scp.py +89 -83
- sky/clouds/seeweb.py +466 -0
- sky/clouds/shadeform.py +400 -0
- sky/clouds/ssh.py +263 -0
- sky/clouds/utils/aws_utils.py +10 -4
- sky/clouds/utils/gcp_utils.py +87 -11
- sky/clouds/utils/oci_utils.py +38 -14
- sky/clouds/utils/scp_utils.py +177 -124
- sky/clouds/vast.py +99 -77
- sky/clouds/vsphere.py +51 -40
- sky/core.py +349 -139
- sky/dag.py +15 -0
- sky/dashboard/out/404.html +1 -1
- sky/dashboard/out/_next/static/chunks/1141-e6aa9ab418717c59.js +11 -0
- sky/dashboard/out/_next/static/chunks/1272-1ef0bf0237faccdb.js +1 -0
- sky/dashboard/out/_next/static/chunks/1871-74503c8e80fd253b.js +6 -0
- sky/dashboard/out/_next/static/chunks/2260-7703229c33c5ebd5.js +1 -0
- sky/dashboard/out/_next/static/chunks/2350.fab69e61bac57b23.js +1 -0
- sky/dashboard/out/_next/static/chunks/2369.fc20f0c2c8ed9fe7.js +15 -0
- sky/dashboard/out/_next/static/chunks/2755.fff53c4a3fcae910.js +26 -0
- sky/dashboard/out/_next/static/chunks/3294.72362fa129305b19.js +1 -0
- sky/dashboard/out/_next/static/chunks/3785.ad6adaa2a0fa9768.js +1 -0
- sky/dashboard/out/_next/static/chunks/3850-ff4a9a69d978632b.js +1 -0
- sky/dashboard/out/_next/static/chunks/3937.210053269f121201.js +1 -0
- sky/dashboard/out/_next/static/chunks/4725.a830b5c9e7867c92.js +1 -0
- sky/dashboard/out/_next/static/chunks/4937.a2baa2df5572a276.js +15 -0
- sky/dashboard/out/_next/static/chunks/5739-d67458fcb1386c92.js +8 -0
- sky/dashboard/out/_next/static/chunks/6130-2be46d70a38f1e82.js +1 -0
- sky/dashboard/out/_next/static/chunks/616-3d59f75e2ccf9321.js +39 -0
- sky/dashboard/out/_next/static/chunks/6212-7bd06f60ba693125.js +13 -0
- sky/dashboard/out/_next/static/chunks/6601-06114c982db410b6.js +1 -0
- sky/dashboard/out/_next/static/chunks/6856-ef8ba11f96d8c4a3.js +1 -0
- sky/dashboard/out/_next/static/chunks/6989-01359c57e018caa4.js +1 -0
- sky/dashboard/out/_next/static/chunks/6990-32b6e2d3822301fa.js +1 -0
- sky/dashboard/out/_next/static/chunks/7359-c8d04e06886000b3.js +30 -0
- sky/dashboard/out/_next/static/chunks/7411-b15471acd2cba716.js +41 -0
- sky/dashboard/out/_next/static/chunks/7615-3301e838e5f25772.js +1 -0
- sky/dashboard/out/_next/static/chunks/8640.5b9475a2d18c5416.js +16 -0
- sky/dashboard/out/_next/static/chunks/8969-1e4613c651bf4051.js +1 -0
- sky/dashboard/out/_next/static/chunks/9025.fa408f3242e9028d.js +6 -0
- sky/dashboard/out/_next/static/chunks/9353-cff34f7e773b2e2b.js +1 -0
- sky/dashboard/out/_next/static/chunks/9360.7310982cf5a0dc79.js +31 -0
- sky/dashboard/out/_next/static/chunks/9847.3aaca6bb33455140.js +30 -0
- sky/dashboard/out/_next/static/chunks/fd9d1056-86323a29a8f7e46a.js +1 -0
- sky/dashboard/out/_next/static/chunks/framework-cf60a09ccd051a10.js +33 -0
- sky/dashboard/out/_next/static/chunks/main-app-587214043926b3cc.js +1 -0
- sky/dashboard/out/_next/static/chunks/main-f15ccb73239a3bf1.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/_app-bde01e4a2beec258.js +34 -0
- sky/dashboard/out/_next/static/chunks/pages/_error-c66a4e8afc46f17b.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]/[job]-c736ead69c2d86ec.js +16 -0
- sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]-a37d2063af475a1c.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/clusters-d44859594e6f8064.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/config-dfb9bf07b13045f4.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/index-444f1804401f04ea.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/infra/[context]-c0b5935149902e6f.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/infra-aed0ea19df7cf961.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/jobs/[job]-5796e8d6aea291a0.js +16 -0
- sky/dashboard/out/_next/static/chunks/pages/jobs/pools/[pool]-6edeb7d06032adfc.js +21 -0
- sky/dashboard/out/_next/static/chunks/pages/jobs-479dde13399cf270.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/users-5ab3b907622cf0fe.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/volumes-b84b948ff357c43e.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/workspace/new-3f88a1c7e86a3f86.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/workspaces/[name]-c5a3eeee1c218af1.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/workspaces-22b23febb3e89ce1.js +1 -0
- sky/dashboard/out/_next/static/chunks/webpack-2679be77fc08a2f8.js +1 -0
- sky/dashboard/out/_next/static/css/0748ce22df867032.css +3 -0
- sky/dashboard/out/_next/static/zB0ed6ge_W1MDszVHhijS/_buildManifest.js +1 -0
- sky/dashboard/out/clusters/[cluster]/[job].html +1 -1
- sky/dashboard/out/clusters/[cluster].html +1 -1
- sky/dashboard/out/clusters.html +1 -1
- sky/dashboard/out/config.html +1 -0
- sky/dashboard/out/index.html +1 -1
- sky/dashboard/out/infra/[context].html +1 -0
- sky/dashboard/out/infra.html +1 -0
- sky/dashboard/out/jobs/[job].html +1 -1
- sky/dashboard/out/jobs/pools/[pool].html +1 -0
- sky/dashboard/out/jobs.html +1 -1
- sky/dashboard/out/users.html +1 -0
- sky/dashboard/out/volumes.html +1 -0
- sky/dashboard/out/workspace/new.html +1 -0
- sky/dashboard/out/workspaces/[name].html +1 -0
- sky/dashboard/out/workspaces.html +1 -0
- sky/data/data_utils.py +137 -1
- sky/data/mounting_utils.py +269 -84
- sky/data/storage.py +1451 -1807
- sky/data/storage_utils.py +43 -57
- sky/exceptions.py +132 -2
- sky/execution.py +206 -63
- sky/global_user_state.py +2374 -586
- sky/jobs/__init__.py +5 -0
- sky/jobs/client/sdk.py +242 -65
- sky/jobs/client/sdk_async.py +143 -0
- sky/jobs/constants.py +9 -8
- sky/jobs/controller.py +839 -277
- sky/jobs/file_content_utils.py +80 -0
- sky/jobs/log_gc.py +201 -0
- sky/jobs/recovery_strategy.py +398 -152
- sky/jobs/scheduler.py +315 -189
- sky/jobs/server/core.py +829 -255
- sky/jobs/server/server.py +156 -115
- sky/jobs/server/utils.py +136 -0
- sky/jobs/state.py +2092 -701
- sky/jobs/utils.py +1242 -160
- sky/logs/__init__.py +21 -0
- sky/logs/agent.py +108 -0
- sky/logs/aws.py +243 -0
- sky/logs/gcp.py +91 -0
- sky/metrics/__init__.py +0 -0
- sky/metrics/utils.py +443 -0
- sky/models.py +78 -1
- sky/optimizer.py +164 -70
- sky/provision/__init__.py +90 -4
- sky/provision/aws/config.py +147 -26
- sky/provision/aws/instance.py +135 -50
- sky/provision/azure/instance.py +10 -5
- sky/provision/common.py +13 -1
- sky/provision/cudo/cudo_machine_type.py +1 -1
- sky/provision/cudo/cudo_utils.py +14 -8
- sky/provision/cudo/cudo_wrapper.py +72 -71
- sky/provision/cudo/instance.py +10 -6
- sky/provision/do/instance.py +10 -6
- sky/provision/do/utils.py +4 -3
- sky/provision/docker_utils.py +114 -23
- sky/provision/fluidstack/instance.py +13 -8
- sky/provision/gcp/__init__.py +1 -0
- sky/provision/gcp/config.py +301 -19
- sky/provision/gcp/constants.py +218 -0
- sky/provision/gcp/instance.py +36 -8
- sky/provision/gcp/instance_utils.py +18 -4
- sky/provision/gcp/volume_utils.py +247 -0
- sky/provision/hyperbolic/__init__.py +12 -0
- sky/provision/hyperbolic/config.py +10 -0
- sky/provision/hyperbolic/instance.py +437 -0
- sky/provision/hyperbolic/utils.py +373 -0
- sky/provision/instance_setup.py +93 -14
- sky/provision/kubernetes/__init__.py +5 -0
- sky/provision/kubernetes/config.py +9 -52
- sky/provision/kubernetes/constants.py +17 -0
- sky/provision/kubernetes/instance.py +789 -247
- sky/provision/kubernetes/manifests/fusermount-server-daemonset.yaml +1 -2
- sky/provision/kubernetes/network.py +27 -17
- sky/provision/kubernetes/network_utils.py +40 -43
- sky/provision/kubernetes/utils.py +1192 -531
- sky/provision/kubernetes/volume.py +282 -0
- sky/provision/lambda_cloud/instance.py +22 -16
- sky/provision/nebius/constants.py +50 -0
- sky/provision/nebius/instance.py +19 -6
- sky/provision/nebius/utils.py +196 -91
- sky/provision/oci/instance.py +10 -5
- sky/provision/paperspace/instance.py +10 -7
- sky/provision/paperspace/utils.py +1 -1
- sky/provision/primeintellect/__init__.py +10 -0
- sky/provision/primeintellect/config.py +11 -0
- sky/provision/primeintellect/instance.py +454 -0
- sky/provision/primeintellect/utils.py +398 -0
- sky/provision/provisioner.py +110 -36
- sky/provision/runpod/__init__.py +5 -0
- sky/provision/runpod/instance.py +27 -6
- sky/provision/runpod/utils.py +51 -18
- sky/provision/runpod/volume.py +180 -0
- sky/provision/scp/__init__.py +15 -0
- sky/provision/scp/config.py +93 -0
- sky/provision/scp/instance.py +531 -0
- sky/provision/seeweb/__init__.py +11 -0
- sky/provision/seeweb/config.py +13 -0
- sky/provision/seeweb/instance.py +807 -0
- sky/provision/shadeform/__init__.py +11 -0
- sky/provision/shadeform/config.py +12 -0
- sky/provision/shadeform/instance.py +351 -0
- sky/provision/shadeform/shadeform_utils.py +83 -0
- sky/provision/ssh/__init__.py +18 -0
- sky/provision/vast/instance.py +13 -8
- sky/provision/vast/utils.py +10 -7
- sky/provision/vsphere/common/vim_utils.py +1 -2
- sky/provision/vsphere/instance.py +15 -10
- sky/provision/vsphere/vsphere_utils.py +9 -19
- sky/py.typed +0 -0
- sky/resources.py +844 -118
- sky/schemas/__init__.py +0 -0
- sky/schemas/api/__init__.py +0 -0
- sky/schemas/api/responses.py +225 -0
- sky/schemas/db/README +4 -0
- sky/schemas/db/env.py +90 -0
- sky/schemas/db/global_user_state/001_initial_schema.py +124 -0
- sky/schemas/db/global_user_state/002_add_workspace_to_cluster_history.py +35 -0
- sky/schemas/db/global_user_state/003_fix_initial_revision.py +61 -0
- sky/schemas/db/global_user_state/004_is_managed.py +34 -0
- sky/schemas/db/global_user_state/005_cluster_event.py +32 -0
- sky/schemas/db/global_user_state/006_provision_log.py +41 -0
- sky/schemas/db/global_user_state/007_cluster_event_request_id.py +34 -0
- sky/schemas/db/global_user_state/008_skylet_ssh_tunnel_metadata.py +34 -0
- sky/schemas/db/global_user_state/009_last_activity_and_launched_at.py +89 -0
- sky/schemas/db/global_user_state/010_save_ssh_key.py +66 -0
- sky/schemas/db/script.py.mako +28 -0
- sky/schemas/db/serve_state/001_initial_schema.py +67 -0
- sky/schemas/db/skypilot_config/001_initial_schema.py +30 -0
- sky/schemas/db/spot_jobs/001_initial_schema.py +97 -0
- sky/schemas/db/spot_jobs/002_cluster_pool.py +42 -0
- sky/schemas/db/spot_jobs/003_pool_hash.py +34 -0
- sky/schemas/db/spot_jobs/004_job_file_contents.py +42 -0
- sky/schemas/db/spot_jobs/005_logs_gc.py +38 -0
- sky/schemas/generated/__init__.py +0 -0
- sky/schemas/generated/autostopv1_pb2.py +36 -0
- sky/schemas/generated/autostopv1_pb2.pyi +43 -0
- sky/schemas/generated/autostopv1_pb2_grpc.py +146 -0
- sky/schemas/generated/jobsv1_pb2.py +86 -0
- sky/schemas/generated/jobsv1_pb2.pyi +254 -0
- sky/schemas/generated/jobsv1_pb2_grpc.py +542 -0
- sky/schemas/generated/managed_jobsv1_pb2.py +74 -0
- sky/schemas/generated/managed_jobsv1_pb2.pyi +278 -0
- sky/schemas/generated/managed_jobsv1_pb2_grpc.py +278 -0
- sky/schemas/generated/servev1_pb2.py +58 -0
- sky/schemas/generated/servev1_pb2.pyi +115 -0
- sky/schemas/generated/servev1_pb2_grpc.py +322 -0
- sky/serve/autoscalers.py +357 -5
- sky/serve/client/impl.py +310 -0
- sky/serve/client/sdk.py +47 -139
- sky/serve/client/sdk_async.py +130 -0
- sky/serve/constants.py +10 -8
- sky/serve/controller.py +64 -19
- sky/serve/load_balancer.py +106 -60
- sky/serve/load_balancing_policies.py +115 -1
- sky/serve/replica_managers.py +273 -162
- sky/serve/serve_rpc_utils.py +179 -0
- sky/serve/serve_state.py +554 -251
- sky/serve/serve_utils.py +733 -220
- sky/serve/server/core.py +66 -711
- sky/serve/server/impl.py +1093 -0
- sky/serve/server/server.py +21 -18
- sky/serve/service.py +133 -48
- sky/serve/service_spec.py +135 -16
- sky/serve/spot_placer.py +3 -0
- sky/server/auth/__init__.py +0 -0
- sky/server/auth/authn.py +50 -0
- sky/server/auth/loopback.py +38 -0
- sky/server/auth/oauth2_proxy.py +200 -0
- sky/server/common.py +475 -181
- sky/server/config.py +81 -23
- sky/server/constants.py +44 -6
- sky/server/daemons.py +229 -0
- sky/server/html/token_page.html +185 -0
- sky/server/metrics.py +160 -0
- sky/server/requests/executor.py +528 -138
- sky/server/requests/payloads.py +351 -17
- sky/server/requests/preconditions.py +21 -17
- sky/server/requests/process.py +112 -29
- sky/server/requests/request_names.py +120 -0
- sky/server/requests/requests.py +817 -224
- sky/server/requests/serializers/decoders.py +82 -31
- sky/server/requests/serializers/encoders.py +140 -22
- sky/server/requests/threads.py +106 -0
- sky/server/rest.py +417 -0
- sky/server/server.py +1290 -284
- sky/server/state.py +20 -0
- sky/server/stream_utils.py +345 -57
- sky/server/uvicorn.py +217 -3
- sky/server/versions.py +270 -0
- sky/setup_files/MANIFEST.in +5 -0
- sky/setup_files/alembic.ini +156 -0
- sky/setup_files/dependencies.py +136 -31
- sky/setup_files/setup.py +44 -42
- sky/sky_logging.py +102 -5
- sky/skylet/attempt_skylet.py +1 -0
- sky/skylet/autostop_lib.py +129 -8
- sky/skylet/configs.py +27 -20
- sky/skylet/constants.py +171 -19
- sky/skylet/events.py +105 -21
- sky/skylet/job_lib.py +335 -104
- sky/skylet/log_lib.py +297 -18
- sky/skylet/log_lib.pyi +44 -1
- sky/skylet/ray_patches/__init__.py +17 -3
- sky/skylet/ray_patches/autoscaler.py.diff +18 -0
- sky/skylet/ray_patches/cli.py.diff +19 -0
- sky/skylet/ray_patches/command_runner.py.diff +17 -0
- sky/skylet/ray_patches/log_monitor.py.diff +20 -0
- sky/skylet/ray_patches/resource_demand_scheduler.py.diff +32 -0
- sky/skylet/ray_patches/updater.py.diff +18 -0
- sky/skylet/ray_patches/worker.py.diff +41 -0
- sky/skylet/services.py +564 -0
- sky/skylet/skylet.py +63 -4
- sky/skylet/subprocess_daemon.py +103 -29
- sky/skypilot_config.py +506 -99
- sky/ssh_node_pools/__init__.py +1 -0
- sky/ssh_node_pools/core.py +135 -0
- sky/ssh_node_pools/server.py +233 -0
- sky/task.py +621 -137
- sky/templates/aws-ray.yml.j2 +10 -3
- sky/templates/azure-ray.yml.j2 +1 -1
- sky/templates/do-ray.yml.j2 +1 -1
- sky/templates/gcp-ray.yml.j2 +57 -0
- sky/templates/hyperbolic-ray.yml.j2 +67 -0
- sky/templates/jobs-controller.yaml.j2 +27 -24
- sky/templates/kubernetes-loadbalancer.yml.j2 +2 -0
- sky/templates/kubernetes-ray.yml.j2 +607 -51
- sky/templates/lambda-ray.yml.j2 +1 -1
- sky/templates/nebius-ray.yml.j2 +33 -12
- sky/templates/paperspace-ray.yml.j2 +1 -1
- sky/templates/primeintellect-ray.yml.j2 +71 -0
- sky/templates/runpod-ray.yml.j2 +9 -1
- sky/templates/scp-ray.yml.j2 +3 -50
- sky/templates/seeweb-ray.yml.j2 +108 -0
- sky/templates/shadeform-ray.yml.j2 +72 -0
- sky/templates/sky-serve-controller.yaml.j2 +22 -2
- sky/templates/websocket_proxy.py +178 -18
- sky/usage/usage_lib.py +18 -11
- sky/users/__init__.py +0 -0
- sky/users/model.conf +15 -0
- sky/users/permission.py +387 -0
- sky/users/rbac.py +121 -0
- sky/users/server.py +720 -0
- sky/users/token_service.py +218 -0
- sky/utils/accelerator_registry.py +34 -5
- sky/utils/admin_policy_utils.py +84 -38
- sky/utils/annotations.py +16 -5
- sky/utils/asyncio_utils.py +78 -0
- sky/utils/auth_utils.py +153 -0
- sky/utils/benchmark_utils.py +60 -0
- sky/utils/cli_utils/status_utils.py +159 -86
- sky/utils/cluster_utils.py +31 -9
- sky/utils/command_runner.py +354 -68
- sky/utils/command_runner.pyi +93 -3
- sky/utils/common.py +35 -8
- sky/utils/common_utils.py +310 -87
- sky/utils/config_utils.py +87 -5
- sky/utils/context.py +402 -0
- sky/utils/context_utils.py +222 -0
- sky/utils/controller_utils.py +264 -89
- sky/utils/dag_utils.py +31 -12
- sky/utils/db/__init__.py +0 -0
- sky/utils/db/db_utils.py +470 -0
- sky/utils/db/migration_utils.py +133 -0
- sky/utils/directory_utils.py +12 -0
- sky/utils/env_options.py +13 -0
- sky/utils/git.py +567 -0
- sky/utils/git_clone.sh +460 -0
- sky/utils/infra_utils.py +195 -0
- sky/utils/kubernetes/cleanup-tunnel.sh +62 -0
- sky/utils/kubernetes/config_map_utils.py +133 -0
- sky/utils/kubernetes/create_cluster.sh +13 -27
- sky/utils/kubernetes/delete_cluster.sh +10 -7
- sky/utils/kubernetes/deploy_remote_cluster.py +1299 -0
- sky/utils/kubernetes/exec_kubeconfig_converter.py +22 -31
- sky/utils/kubernetes/generate_kind_config.py +6 -66
- sky/utils/kubernetes/generate_kubeconfig.sh +4 -1
- sky/utils/kubernetes/gpu_labeler.py +5 -5
- sky/utils/kubernetes/kubernetes_deploy_utils.py +354 -47
- sky/utils/kubernetes/ssh-tunnel.sh +379 -0
- sky/utils/kubernetes/ssh_utils.py +221 -0
- sky/utils/kubernetes_enums.py +8 -15
- sky/utils/lock_events.py +94 -0
- sky/utils/locks.py +368 -0
- sky/utils/log_utils.py +300 -6
- sky/utils/perf_utils.py +22 -0
- sky/utils/resource_checker.py +298 -0
- sky/utils/resources_utils.py +249 -32
- sky/utils/rich_utils.py +213 -37
- sky/utils/schemas.py +905 -147
- sky/utils/serialize_utils.py +16 -0
- sky/utils/status_lib.py +10 -0
- sky/utils/subprocess_utils.py +38 -15
- sky/utils/tempstore.py +70 -0
- sky/utils/timeline.py +24 -52
- sky/utils/ux_utils.py +84 -15
- sky/utils/validator.py +11 -1
- sky/utils/volume.py +86 -0
- sky/utils/yaml_utils.py +111 -0
- sky/volumes/__init__.py +13 -0
- sky/volumes/client/__init__.py +0 -0
- sky/volumes/client/sdk.py +149 -0
- sky/volumes/server/__init__.py +0 -0
- sky/volumes/server/core.py +258 -0
- sky/volumes/server/server.py +122 -0
- sky/volumes/volume.py +212 -0
- sky/workspaces/__init__.py +0 -0
- sky/workspaces/core.py +655 -0
- sky/workspaces/server.py +101 -0
- sky/workspaces/utils.py +56 -0
- skypilot_nightly-1.0.0.dev20251107.dist-info/METADATA +675 -0
- skypilot_nightly-1.0.0.dev20251107.dist-info/RECORD +594 -0
- {skypilot_nightly-1.0.0.dev20250509.dist-info → skypilot_nightly-1.0.0.dev20251107.dist-info}/WHEEL +1 -1
- sky/benchmark/benchmark_state.py +0 -256
- sky/benchmark/benchmark_utils.py +0 -641
- sky/clouds/service_catalog/constants.py +0 -7
- sky/dashboard/out/_next/static/LksQgChY5izXjokL3LcEu/_buildManifest.js +0 -1
- sky/dashboard/out/_next/static/chunks/236-f49500b82ad5392d.js +0 -6
- sky/dashboard/out/_next/static/chunks/312-c3c8845990db8ffc.js +0 -15
- sky/dashboard/out/_next/static/chunks/37-0a572fe0dbb89c4d.js +0 -6
- sky/dashboard/out/_next/static/chunks/678-206dddca808e6d16.js +0 -59
- sky/dashboard/out/_next/static/chunks/845-0f8017370869e269.js +0 -1
- sky/dashboard/out/_next/static/chunks/979-7bf73a4c7cea0f5c.js +0 -1
- sky/dashboard/out/_next/static/chunks/fd9d1056-2821b0f0cabcd8bd.js +0 -1
- sky/dashboard/out/_next/static/chunks/framework-87d061ee6ed71b28.js +0 -33
- sky/dashboard/out/_next/static/chunks/main-app-241eb28595532291.js +0 -1
- sky/dashboard/out/_next/static/chunks/main-e0e2335212e72357.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/_app-e6b013bc3f77ad60.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/_error-1be831200e60c5c0.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]/[job]-e15db85d0ea1fbe1.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]-f383db7389368ea7.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/clusters-a93b93e10b8b074e.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/index-f9f039532ca8cbc4.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/jobs/[job]-03f279c6741fb48b.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/jobs-a75029b67aab6a2e.js +0 -1
- sky/dashboard/out/_next/static/chunks/webpack-830f59b8404e96b8.js +0 -1
- sky/dashboard/out/_next/static/css/c6933bbb2ce7f4dd.css +0 -3
- sky/jobs/dashboard/dashboard.py +0 -223
- sky/jobs/dashboard/static/favicon.ico +0 -0
- sky/jobs/dashboard/templates/index.html +0 -831
- sky/jobs/server/dashboard_utils.py +0 -69
- sky/skylet/providers/scp/__init__.py +0 -2
- sky/skylet/providers/scp/config.py +0 -149
- sky/skylet/providers/scp/node_provider.py +0 -578
- sky/templates/kubernetes-ssh-jump.yml.j2 +0 -94
- sky/utils/db_utils.py +0 -100
- sky/utils/kubernetes/deploy_remote_cluster.sh +0 -308
- sky/utils/kubernetes/ssh_jump_lifecycle_manager.py +0 -191
- skypilot_nightly-1.0.0.dev20250509.dist-info/METADATA +0 -361
- skypilot_nightly-1.0.0.dev20250509.dist-info/RECORD +0 -396
- /sky/{clouds/service_catalog → catalog}/config.py +0 -0
- /sky/{benchmark → catalog/data_fetchers}/__init__.py +0 -0
- /sky/{clouds/service_catalog → catalog}/data_fetchers/fetch_azure.py +0 -0
- /sky/{clouds/service_catalog → catalog}/data_fetchers/fetch_fluidstack.py +0 -0
- /sky/{clouds/service_catalog → catalog}/data_fetchers/fetch_ibm.py +0 -0
- /sky/{clouds/service_catalog/data_fetchers → client/cli}/__init__.py +0 -0
- /sky/dashboard/out/_next/static/{LksQgChY5izXjokL3LcEu → zB0ed6ge_W1MDszVHhijS}/_ssgManifest.js +0 -0
- {skypilot_nightly-1.0.0.dev20250509.dist-info → skypilot_nightly-1.0.0.dev20251107.dist-info}/entry_points.txt +0 -0
- {skypilot_nightly-1.0.0.dev20250509.dist-info → skypilot_nightly-1.0.0.dev20251107.dist-info}/licenses/LICENSE +0 -0
- {skypilot_nightly-1.0.0.dev20250509.dist-info → skypilot_nightly-1.0.0.dev20251107.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
"""Prime Intellect library wrapper for SkyPilot."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
import shlex
|
|
6
|
+
import time
|
|
7
|
+
from typing import Any, Dict, List, Optional, Tuple, Union
|
|
8
|
+
import uuid
|
|
9
|
+
|
|
10
|
+
import requests
|
|
11
|
+
|
|
12
|
+
from sky.catalog import common as catalog_common
|
|
13
|
+
from sky.utils import common_utils
|
|
14
|
+
|
|
15
|
+
_df = None
|
|
16
|
+
_lookup_dict = None
|
|
17
|
+
|
|
18
|
+
# Base URL for Prime Intellect API (used as default if not configured).
|
|
19
|
+
DEFAULT_BASE_URL = 'https://api.primeintellect.ai'
|
|
20
|
+
CREDENTIALS_PATH = '~/.prime/config.json'
|
|
21
|
+
INITIAL_BACKOFF_SECONDS = 10
|
|
22
|
+
MAX_BACKOFF_FACTOR = 10
|
|
23
|
+
MAX_ATTEMPTS = 6
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class PrimeintellectAPIError(Exception):
|
|
27
|
+
"""Base exception for Prime Intellect API errors."""
|
|
28
|
+
|
|
29
|
+
def __init__(self,
|
|
30
|
+
message: str,
|
|
31
|
+
status_code: Optional[int] = None,
|
|
32
|
+
response_data: Optional[Dict[str, Any]] = None):
|
|
33
|
+
super().__init__(message)
|
|
34
|
+
self.status_code = status_code
|
|
35
|
+
self.response_data = response_data
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class PrimeintellectResourcesUnavailableError(PrimeintellectAPIError):
|
|
39
|
+
"""Exception for when resources are unavailable on Prime Intellect."""
|
|
40
|
+
pass
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _parse_api_error(response: Any) -> Tuple[str, bool]:
|
|
44
|
+
"""Parse API error response to extract meaningful error messages.
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
Tuple[str, bool]:
|
|
48
|
+
- str: A human-readable error message parsed from the API response.
|
|
49
|
+
- bool: True if the error indicates resource unavailability (e.g.,
|
|
50
|
+
capacity issues or quota/limit exceeded), otherwise False.
|
|
51
|
+
"""
|
|
52
|
+
try:
|
|
53
|
+
if hasattr(response, 'json'):
|
|
54
|
+
error_data = response.json()
|
|
55
|
+
else:
|
|
56
|
+
error_data = response
|
|
57
|
+
|
|
58
|
+
if isinstance(error_data, dict):
|
|
59
|
+
# Try to extract error message from common error response fields
|
|
60
|
+
error_message = error_data.get('message', '')
|
|
61
|
+
if not error_message:
|
|
62
|
+
error_message = error_data.get('error', '')
|
|
63
|
+
if not error_message:
|
|
64
|
+
error_message = error_data.get('detail', '')
|
|
65
|
+
|
|
66
|
+
# Check if it's a resource unavailability error
|
|
67
|
+
if any(keyword in error_message.lower() for keyword in [
|
|
68
|
+
'no capacity', 'capacity', 'unavailable', 'out of stock',
|
|
69
|
+
'insufficient', 'not available', 'quota exceeded',
|
|
70
|
+
'limit exceeded'
|
|
71
|
+
]):
|
|
72
|
+
return error_message, True
|
|
73
|
+
|
|
74
|
+
return error_message, False
|
|
75
|
+
|
|
76
|
+
return str(error_data), False
|
|
77
|
+
except Exception: # pylint: disable=broad-except
|
|
78
|
+
return f'HTTP {response.status_code} {response.reason}', False
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _try_request_with_backoff(
|
|
82
|
+
method: str,
|
|
83
|
+
url: str,
|
|
84
|
+
headers: Dict[str, str],
|
|
85
|
+
data: Optional[Union[str, Dict[str, Any]]] = None) -> Dict[str, Any]:
|
|
86
|
+
backoff = common_utils.Backoff(initial_backoff=INITIAL_BACKOFF_SECONDS,
|
|
87
|
+
max_backoff_factor=MAX_BACKOFF_FACTOR)
|
|
88
|
+
for i in range(MAX_ATTEMPTS):
|
|
89
|
+
timeout = 30
|
|
90
|
+
if method == 'get':
|
|
91
|
+
response = requests.get(url,
|
|
92
|
+
headers=headers,
|
|
93
|
+
params=data,
|
|
94
|
+
timeout=timeout)
|
|
95
|
+
elif method == 'post':
|
|
96
|
+
response = requests.post(url,
|
|
97
|
+
headers=headers,
|
|
98
|
+
json=data,
|
|
99
|
+
timeout=timeout)
|
|
100
|
+
elif method == 'put':
|
|
101
|
+
response = requests.put(url,
|
|
102
|
+
headers=headers,
|
|
103
|
+
json=data,
|
|
104
|
+
timeout=timeout)
|
|
105
|
+
elif method == 'patch':
|
|
106
|
+
response = requests.patch(url,
|
|
107
|
+
headers=headers,
|
|
108
|
+
json=data,
|
|
109
|
+
timeout=timeout)
|
|
110
|
+
elif method == 'delete':
|
|
111
|
+
response = requests.delete(url, headers=headers, timeout=timeout)
|
|
112
|
+
else:
|
|
113
|
+
raise ValueError(f'Unsupported requests method: {method}')
|
|
114
|
+
# If rate limited, wait and try again
|
|
115
|
+
if response.status_code == 429 and i != MAX_ATTEMPTS - 1:
|
|
116
|
+
time.sleep(backoff.current_backoff())
|
|
117
|
+
continue
|
|
118
|
+
if response.ok:
|
|
119
|
+
return response.json()
|
|
120
|
+
else:
|
|
121
|
+
# Parse the error response for meaningful messages
|
|
122
|
+
err, is_resource_unavailable = _parse_api_error(response)
|
|
123
|
+
|
|
124
|
+
# Create a more informative error message
|
|
125
|
+
if not err:
|
|
126
|
+
err = (f'API request failed: {method} {url}: '
|
|
127
|
+
f'{response.status_code} {response.reason}')
|
|
128
|
+
else:
|
|
129
|
+
err = f'API request failed: {err}'
|
|
130
|
+
|
|
131
|
+
# Raise appropriate exception based on error type
|
|
132
|
+
if is_resource_unavailable:
|
|
133
|
+
raise PrimeintellectResourcesUnavailableError(
|
|
134
|
+
err,
|
|
135
|
+
status_code=response.status_code,
|
|
136
|
+
response_data=response.json()
|
|
137
|
+
if hasattr(response, 'json') else None)
|
|
138
|
+
else:
|
|
139
|
+
raise PrimeintellectAPIError(
|
|
140
|
+
err,
|
|
141
|
+
status_code=response.status_code,
|
|
142
|
+
response_data=response.json()
|
|
143
|
+
if hasattr(response, 'json') else None)
|
|
144
|
+
return {}
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def get_upstream_cloud_id(instance_type: str) -> Optional[str]:
|
|
148
|
+
global _df, _lookup_dict
|
|
149
|
+
if _df is None:
|
|
150
|
+
_df = catalog_common.read_catalog('primeintellect/vms.csv')
|
|
151
|
+
_lookup_dict = (
|
|
152
|
+
_df.set_index('InstanceType')['UpstreamCloudId'].to_dict())
|
|
153
|
+
return _lookup_dict.get(instance_type)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
class PrimeIntellectAPIClient:
|
|
157
|
+
"""Client for interacting with Prime Intellect API."""
|
|
158
|
+
|
|
159
|
+
def __init__(self) -> None:
|
|
160
|
+
self.credentials = os.path.expanduser(CREDENTIALS_PATH)
|
|
161
|
+
assert os.path.exists(self.credentials), 'Credentials not found'
|
|
162
|
+
with open(self.credentials, 'r', encoding='utf-8') as f:
|
|
163
|
+
self._credentials = json.load(f)
|
|
164
|
+
self.api_key = self._credentials.get('api_key')
|
|
165
|
+
self.team_id = self._credentials.get('team_id')
|
|
166
|
+
self.base_url = self._credentials.get('base_url', DEFAULT_BASE_URL)
|
|
167
|
+
self.headers = {
|
|
168
|
+
'Authorization': f'Bearer {self.api_key}',
|
|
169
|
+
'Content-Type': 'application/json'
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
def list_instances(self, **search_kwargs) -> List[Dict[str, Any]]:
|
|
173
|
+
response = _try_request_with_backoff('get',
|
|
174
|
+
f'{self.base_url}/api/v1/pods',
|
|
175
|
+
headers=self.headers,
|
|
176
|
+
data=search_kwargs)
|
|
177
|
+
return response['data']
|
|
178
|
+
|
|
179
|
+
def get_instance_details(self, instance_id: str) -> Dict[str, Any]:
|
|
180
|
+
return _try_request_with_backoff(
|
|
181
|
+
'get',
|
|
182
|
+
f'{self.base_url}/api/v1/pods/{instance_id}',
|
|
183
|
+
headers=self.headers)
|
|
184
|
+
|
|
185
|
+
def launch(self,
|
|
186
|
+
name: str,
|
|
187
|
+
instance_type: str,
|
|
188
|
+
region: str,
|
|
189
|
+
availability_zone: str,
|
|
190
|
+
disk_size: int,
|
|
191
|
+
vcpus: int = 0,
|
|
192
|
+
memory: int = 0) -> Dict[str, Any]:
|
|
193
|
+
"""Create a pod/instance via Prime Intellect API.
|
|
194
|
+
|
|
195
|
+
Args:
|
|
196
|
+
name: User-visible name of the pod.
|
|
197
|
+
instance_type: A catalog instance type string. The expected format
|
|
198
|
+
is:
|
|
199
|
+
"<provider>__<accelerator>__<vcpus>__<memory>[_SPOT]".
|
|
200
|
+
|
|
201
|
+
- <provider>: Upstream provider tag (e.g., "primecompute").
|
|
202
|
+
- <accelerator>:
|
|
203
|
+
* GPU nodes: "<N>x<GPU_MODEL>", e.g., "8xH100_80GB".
|
|
204
|
+
* CPU-only nodes: the literal "CPU_NODE".
|
|
205
|
+
- <vcpus>: Integer string for vCPU count (e.g., "104").
|
|
206
|
+
- <memory>: Integer string for memory in GB (e.g., "752").
|
|
207
|
+
- Optional suffix "_SPOT" may be present in the full string
|
|
208
|
+
(ignored here; pricing/spot behavior is not controlled by
|
|
209
|
+
this method).
|
|
210
|
+
|
|
211
|
+
Notes:
|
|
212
|
+
- Parsing: only the first two components (provider,
|
|
213
|
+
accelerator) are needed to build the payload. The vCPU
|
|
214
|
+
and memory values are provided via the ``vcpus`` and
|
|
215
|
+
``memory`` arguments.
|
|
216
|
+
- Catalog lookup: the full instance_type string is used to
|
|
217
|
+
map to the catalog's UpstreamCloudId.
|
|
218
|
+
- CPU-only: accelerator "CPU_NODE" is a sentinel for
|
|
219
|
+
"no GPU". We set gpuType='CPU_NODE' and gpuCount=1 to
|
|
220
|
+
represent CPU-only pods.
|
|
221
|
+
- Spot: the optional "__SPOT" suffix (if present) is ignored
|
|
222
|
+
here; pricing/spot behavior is handled elsewhere.
|
|
223
|
+
|
|
224
|
+
region: Country/region code used by Prime Intellect.
|
|
225
|
+
availability_zone: Data center ID (zone) within the region.
|
|
226
|
+
disk_size: Boot disk size in GB.
|
|
227
|
+
vcpus: Optional explicit vCPU override; if >0 it will be sent.
|
|
228
|
+
memory: Optional explicit memory override in GB; if >0 it will be
|
|
229
|
+
sent.
|
|
230
|
+
|
|
231
|
+
Returns:
|
|
232
|
+
The API response JSON as a dict.
|
|
233
|
+
"""
|
|
234
|
+
cloud_id = get_upstream_cloud_id(instance_type)
|
|
235
|
+
assert cloud_id, 'cloudId cannot be None'
|
|
236
|
+
assert availability_zone, 'availability_zone cannot be None'
|
|
237
|
+
|
|
238
|
+
# Parse the instance_type. We only need the first two components:
|
|
239
|
+
# provider and accelerator info (see docstring above).
|
|
240
|
+
provider, gpu_parts, _, _ = instance_type.split('__', 3)
|
|
241
|
+
if 'CPU_NODE' in gpu_parts:
|
|
242
|
+
# Prime Intellect API uses the same schema for CPU-only and GPU
|
|
243
|
+
# pods. For CPU-only instances, we set gpuType='CPU_NODE' and
|
|
244
|
+
# gpuCount=1 as a sentinel to indicate "no GPU". This is how CPU
|
|
245
|
+
# instances are represented internally on our platform; the
|
|
246
|
+
# backend does not interpret this as having a physical GPU.
|
|
247
|
+
gpu_type = 'CPU_NODE'
|
|
248
|
+
gpu_count = 1
|
|
249
|
+
else:
|
|
250
|
+
parts = gpu_parts.split('x', 1)
|
|
251
|
+
gpu_count = int(parts[0])
|
|
252
|
+
gpu_type = parts[1]
|
|
253
|
+
|
|
254
|
+
payload: Dict[str, Any] = {
|
|
255
|
+
'pod': {
|
|
256
|
+
'name': name,
|
|
257
|
+
'cloudId': cloud_id,
|
|
258
|
+
'socket': 'PCIe',
|
|
259
|
+
'gpuType': gpu_type,
|
|
260
|
+
'gpuCount': int(gpu_count),
|
|
261
|
+
'diskSize': disk_size,
|
|
262
|
+
# Prime Intellect API historically required maxPrice.
|
|
263
|
+
# Set to 0 to indicate on-demand/non-spot pricing.
|
|
264
|
+
'maxPrice': 0,
|
|
265
|
+
},
|
|
266
|
+
'provider': {
|
|
267
|
+
'type': provider,
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if vcpus > 0:
|
|
272
|
+
payload['pod']['vcpus'] = vcpus
|
|
273
|
+
if memory > 0:
|
|
274
|
+
payload['pod']['memory'] = memory
|
|
275
|
+
|
|
276
|
+
if region != 'UNSPECIFIED':
|
|
277
|
+
payload['pod']['country'] = region
|
|
278
|
+
if availability_zone != 'UNSPECIFIED':
|
|
279
|
+
payload['pod']['dataCenterId'] = availability_zone
|
|
280
|
+
|
|
281
|
+
if self.team_id is not None and self.team_id != '':
|
|
282
|
+
payload['team'] = {'teamId': self.team_id}
|
|
283
|
+
|
|
284
|
+
response = _try_request_with_backoff(
|
|
285
|
+
'post',
|
|
286
|
+
f'{self.base_url}/api/v1/pods',
|
|
287
|
+
headers=self.headers,
|
|
288
|
+
data=payload,
|
|
289
|
+
)
|
|
290
|
+
return response
|
|
291
|
+
|
|
292
|
+
def remove(self, instance_id: str) -> Dict[str, Any]:
|
|
293
|
+
return _try_request_with_backoff(
|
|
294
|
+
'delete',
|
|
295
|
+
f'{self.base_url}/api/v1/pods/{instance_id}',
|
|
296
|
+
headers=self.headers,
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
def list_ssh_keys(self) -> List[Dict[str, Any]]:
|
|
300
|
+
response = _try_request_with_backoff('get',
|
|
301
|
+
f'{self.base_url}/api/v1/ssh_keys',
|
|
302
|
+
headers=self.headers)
|
|
303
|
+
return response['data']
|
|
304
|
+
|
|
305
|
+
def get_or_add_ssh_key(self, ssh_pub_key: str = '') -> Dict[str, str]:
|
|
306
|
+
"""Add ssh key if not already added."""
|
|
307
|
+
# Check if the public key is already added
|
|
308
|
+
ssh_keys = self.list_ssh_keys()
|
|
309
|
+
for key in ssh_keys:
|
|
310
|
+
if key['publicKey'].strip().split()[:2] == ssh_pub_key.strip(
|
|
311
|
+
).split()[:2]:
|
|
312
|
+
return {'name': key['name'], 'ssh_key': ssh_pub_key}
|
|
313
|
+
|
|
314
|
+
# Add the public key to Prime Intellect account if not already added
|
|
315
|
+
ssh_key_name = 'skypilot-' + str(uuid.uuid4()).replace('-', '')[:8]
|
|
316
|
+
_try_request_with_backoff(
|
|
317
|
+
'post',
|
|
318
|
+
f'{self.base_url}/api/v1/ssh_keys',
|
|
319
|
+
headers=self.headers,
|
|
320
|
+
data={
|
|
321
|
+
'name': ssh_key_name,
|
|
322
|
+
'publicKey': ssh_pub_key
|
|
323
|
+
},
|
|
324
|
+
)
|
|
325
|
+
return {'name': ssh_key_name, 'ssh_key': ssh_pub_key}
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def parse_ssh_connection(ssh_connection: Any) -> Tuple[Optional[str], int]:
|
|
329
|
+
"""Parse and extract SSH username and port from a connection field.
|
|
330
|
+
|
|
331
|
+
The provider may return the SSH connection in multiple shapes. This helper
|
|
332
|
+
robustly extracts the SSH username and port while tolerating extra flags or
|
|
333
|
+
various tokenizations.
|
|
334
|
+
|
|
335
|
+
Accepted formats (examples):
|
|
336
|
+
- String with port flag:
|
|
337
|
+
"ubuntu@1.2.3.4 -p 2222 [-o <flag> ...]"
|
|
338
|
+
- String without explicit port (defaults to 22):
|
|
339
|
+
"ubuntu@1.2.3.4"
|
|
340
|
+
- String with host:port:
|
|
341
|
+
"ubuntu@1.2.3.4:2222"
|
|
342
|
+
- List with a single target:
|
|
343
|
+
["ubuntu@1.2.3.4"]
|
|
344
|
+
- List of tokens (e.g., split form):
|
|
345
|
+
["ubuntu@1.2.3.4", "-p", "2222"]
|
|
346
|
+
|
|
347
|
+
Args:
|
|
348
|
+
ssh_connection: The raw field from the API; can be a string or a list
|
|
349
|
+
of strings.
|
|
350
|
+
|
|
351
|
+
Returns:
|
|
352
|
+
(ssh_user, ssh_port): username if found, else None; port if found,
|
|
353
|
+
else 22.
|
|
354
|
+
"""
|
|
355
|
+
ssh_user: Optional[str] = None
|
|
356
|
+
ssh_port: int = 22
|
|
357
|
+
|
|
358
|
+
# Normalize into a list of tokens for easier processing.
|
|
359
|
+
tokens: List[str] = []
|
|
360
|
+
if isinstance(ssh_connection, str):
|
|
361
|
+
try:
|
|
362
|
+
tokens = shlex.split(ssh_connection)
|
|
363
|
+
except Exception: # pylint: disable=broad-except
|
|
364
|
+
tokens = [ssh_connection]
|
|
365
|
+
elif isinstance(ssh_connection, list):
|
|
366
|
+
for elem in ssh_connection:
|
|
367
|
+
if isinstance(elem, str):
|
|
368
|
+
try:
|
|
369
|
+
tokens.extend(shlex.split(elem))
|
|
370
|
+
except Exception: # pylint: disable=broad-except
|
|
371
|
+
tokens.append(elem)
|
|
372
|
+
else:
|
|
373
|
+
# Unknown type; return defaults.
|
|
374
|
+
return ssh_user, ssh_port
|
|
375
|
+
|
|
376
|
+
# Find the first token containing '@' as the user@host candidate.
|
|
377
|
+
user_host: Optional[str] = next((t for t in tokens if '@' in t), None)
|
|
378
|
+
if user_host:
|
|
379
|
+
ssh_user = user_host.split('@', 1)[0].strip()
|
|
380
|
+
# Try host:port format (after '@').
|
|
381
|
+
host_part = user_host.split('@', 1)[1]
|
|
382
|
+
if ':' in host_part:
|
|
383
|
+
_, maybe_port = host_part.rsplit(':', 1)
|
|
384
|
+
try:
|
|
385
|
+
ssh_port = int(maybe_port)
|
|
386
|
+
except ValueError:
|
|
387
|
+
pass
|
|
388
|
+
|
|
389
|
+
# Check for '-p <port>' pair anywhere in the tokens. This takes priority.
|
|
390
|
+
if '-p' in tokens:
|
|
391
|
+
idx = tokens.index('-p')
|
|
392
|
+
if idx + 1 < len(tokens):
|
|
393
|
+
try:
|
|
394
|
+
ssh_port = int(tokens[idx + 1])
|
|
395
|
+
except ValueError:
|
|
396
|
+
pass
|
|
397
|
+
|
|
398
|
+
return ssh_user, ssh_port
|