skypilot-nightly 1.0.0.dev20250502__py3-none-any.whl → 1.0.0.dev20251203__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.
- sky/__init__.py +22 -6
- sky/adaptors/aws.py +81 -16
- sky/adaptors/common.py +25 -2
- sky/adaptors/coreweave.py +278 -0
- sky/adaptors/do.py +8 -2
- sky/adaptors/gcp.py +11 -0
- sky/adaptors/hyperbolic.py +8 -0
- sky/adaptors/ibm.py +5 -2
- sky/adaptors/kubernetes.py +149 -18
- sky/adaptors/nebius.py +173 -30
- sky/adaptors/primeintellect.py +1 -0
- sky/adaptors/runpod.py +68 -0
- sky/adaptors/seeweb.py +183 -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 +1323 -397
- sky/backends/cloud_vm_ray_backend.py +1749 -1029
- sky/backends/docker_utils.py +1 -1
- sky/backends/local_docker_backend.py +11 -6
- sky/backends/task_codegen.py +633 -0
- sky/backends/wheel_utils.py +55 -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 +90 -49
- 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 +116 -80
- sky/{clouds/service_catalog → catalog}/data_fetchers/fetch_cudo.py +38 -38
- sky/{clouds/service_catalog → catalog}/data_fetchers/fetch_gcp.py +70 -16
- 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 +338 -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 +533 -185
- sky/cli.py +5 -5975
- sky/client/{cli.py → cli/command.py} +2591 -1956
- sky/client/cli/deprecation_utils.py +99 -0
- sky/client/cli/flags.py +359 -0
- sky/client/cli/table_utils.py +322 -0
- sky/client/cli/utils.py +79 -0
- sky/client/common.py +78 -32
- sky/client/oauth.py +82 -0
- sky/client/sdk.py +1219 -319
- sky/client/sdk_async.py +827 -0
- sky/client/service_account_auth.py +47 -0
- sky/cloud_stores.py +82 -3
- sky/clouds/__init__.py +13 -0
- sky/clouds/aws.py +564 -164
- sky/clouds/azure.py +105 -83
- sky/clouds/cloud.py +140 -40
- 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 +570 -162
- sky/clouds/lambda_cloud.py +74 -54
- sky/clouds/nebius.py +210 -81
- 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 -86
- sky/clouds/seeweb.py +477 -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 +231 -167
- sky/clouds/vast.py +99 -77
- sky/clouds/vsphere.py +51 -40
- sky/core.py +375 -173
- sky/dag.py +15 -0
- sky/dashboard/out/404.html +1 -1
- sky/dashboard/out/_next/static/96_E2yl3QAiIJGOYCkSpB/_buildManifest.js +1 -0
- 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-7e202677c42f43fe.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.edd818326d489a1d.js +26 -0
- sky/dashboard/out/_next/static/chunks/3294.20a8540fe697d5ee.js +1 -0
- sky/dashboard/out/_next/static/chunks/3785.7e245f318f9d1121.js +1 -0
- sky/dashboard/out/_next/static/chunks/3800-7b45f9fbb6308557.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.172ede95d1b21022.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/6856-8f27d1c10c98def8.js +1 -0
- sky/dashboard/out/_next/static/chunks/6989-01359c57e018caa4.js +1 -0
- sky/dashboard/out/_next/static/chunks/6990-9146207c4567fdfd.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-019513abc55b3b47.js +1 -0
- sky/dashboard/out/_next/static/chunks/8640.5b9475a2d18c5416.js +16 -0
- sky/dashboard/out/_next/static/chunks/8969-452f9d5cbdd2dc73.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.a536cf6b1fa42355.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]-792db96d918c98c9.js +16 -0
- sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]-abfcac9c137aa543.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/clusters-ee39056f9851a3ff.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]-d66997e2bfc837cf.js +16 -0
- sky/dashboard/out/_next/static/chunks/pages/jobs/pools/[pool]-9faf940b253e3e06.js +21 -0
- sky/dashboard/out/_next/static/chunks/pages/jobs-2072b48b617989c9.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/users-f42674164aa73423.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]-84a40f8c7c627fe4.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/workspaces-531b2f8c4bf89f82.js +1 -0
- sky/dashboard/out/_next/static/chunks/webpack-64e05f17bf2cf8ce.js +1 -0
- sky/dashboard/out/_next/static/css/0748ce22df867032.css +3 -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 +1460 -1807
- sky/data/storage_utils.py +43 -57
- sky/exceptions.py +126 -2
- sky/execution.py +216 -63
- sky/global_user_state.py +2390 -586
- sky/jobs/__init__.py +7 -0
- sky/jobs/client/sdk.py +300 -58
- sky/jobs/client/sdk_async.py +161 -0
- sky/jobs/constants.py +15 -8
- sky/jobs/controller.py +848 -275
- sky/jobs/file_content_utils.py +128 -0
- sky/jobs/log_gc.py +193 -0
- sky/jobs/recovery_strategy.py +402 -152
- sky/jobs/scheduler.py +314 -189
- sky/jobs/server/core.py +836 -255
- sky/jobs/server/server.py +156 -115
- sky/jobs/server/utils.py +136 -0
- sky/jobs/state.py +2109 -706
- sky/jobs/utils.py +1306 -215
- 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 +453 -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 +136 -50
- sky/provision/azure/instance.py +11 -6
- 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 +140 -33
- 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 +101 -20
- 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 +919 -280
- sky/provision/kubernetes/manifests/fusermount-server-daemonset.yaml +1 -2
- sky/provision/kubernetes/network.py +27 -17
- sky/provision/kubernetes/network_utils.py +44 -43
- sky/provision/kubernetes/utils.py +1221 -534
- sky/provision/kubernetes/volume.py +343 -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 +237 -137
- 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 +117 -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 +214 -0
- sky/provision/scp/__init__.py +15 -0
- sky/provision/scp/config.py +93 -0
- sky/provision/scp/instance.py +707 -0
- sky/provision/seeweb/__init__.py +11 -0
- sky/provision/seeweb/config.py +13 -0
- sky/provision/seeweb/instance.py +812 -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/volume.py +164 -0
- sky/provision/vsphere/common/ssl_helper.py +1 -1
- sky/provision/vsphere/common/vapiconnect.py +2 -1
- sky/provision/vsphere/common/vim_utils.py +4 -4
- sky/provision/vsphere/instance.py +15 -10
- sky/provision/vsphere/vsphere_utils.py +17 -20
- sky/py.typed +0 -0
- sky/resources.py +845 -119
- sky/schemas/__init__.py +0 -0
- sky/schemas/api/__init__.py +0 -0
- sky/schemas/api/responses.py +227 -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/global_user_state/011_is_ephemeral.py +34 -0
- sky/schemas/db/kv_cache/001_initial_schema.py +29 -0
- sky/schemas/db/script.py.mako +28 -0
- sky/schemas/db/serve_state/001_initial_schema.py +67 -0
- sky/schemas/db/serve_state/002_yaml_content.py +34 -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/db/spot_jobs/006_controller_pid_started_at.py +34 -0
- sky/schemas/db/spot_jobs/007_config_file_content.py +34 -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 +76 -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 +12 -9
- sky/serve/controller.py +68 -17
- sky/serve/load_balancer.py +106 -60
- sky/serve/load_balancing_policies.py +116 -2
- sky/serve/replica_managers.py +434 -249
- sky/serve/serve_rpc_utils.py +179 -0
- sky/serve/serve_state.py +569 -257
- sky/serve/serve_utils.py +775 -265
- 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 +192 -89
- sky/serve/service_spec.py +144 -20
- 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 +202 -0
- sky/server/common.py +478 -182
- sky/server/config.py +85 -23
- sky/server/constants.py +44 -6
- sky/server/daemons.py +295 -0
- sky/server/html/token_page.html +185 -0
- sky/server/metrics.py +160 -0
- sky/server/middleware_utils.py +166 -0
- sky/server/requests/executor.py +558 -138
- sky/server/requests/payloads.py +364 -24
- sky/server/requests/preconditions.py +21 -17
- sky/server/requests/process.py +112 -29
- sky/server/requests/request_names.py +121 -0
- sky/server/requests/requests.py +822 -226
- sky/server/requests/serializers/decoders.py +82 -31
- sky/server/requests/serializers/encoders.py +140 -22
- sky/server/requests/threads.py +117 -0
- sky/server/rest.py +455 -0
- sky/server/server.py +1309 -285
- sky/server/state.py +20 -0
- sky/server/stream_utils.py +327 -61
- sky/server/uvicorn.py +217 -3
- sky/server/versions.py +270 -0
- sky/setup_files/MANIFEST.in +11 -1
- sky/setup_files/alembic.ini +160 -0
- sky/setup_files/dependencies.py +139 -31
- sky/setup_files/setup.py +44 -42
- sky/sky_logging.py +114 -7
- sky/skylet/attempt_skylet.py +106 -24
- sky/skylet/autostop_lib.py +129 -8
- sky/skylet/configs.py +29 -20
- sky/skylet/constants.py +216 -25
- sky/skylet/events.py +101 -21
- sky/skylet/job_lib.py +345 -164
- sky/skylet/log_lib.py +297 -18
- sky/skylet/log_lib.pyi +44 -1
- sky/skylet/providers/ibm/node_provider.py +12 -8
- sky/skylet/providers/ibm/vpc_provider.py +13 -12
- 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/runtime_utils.py +21 -0
- sky/skylet/services.py +568 -0
- sky/skylet/skylet.py +72 -4
- sky/skylet/subprocess_daemon.py +104 -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 +685 -163
- sky/templates/aws-ray.yml.j2 +11 -3
- sky/templates/azure-ray.yml.j2 +2 -1
- sky/templates/cudo-ray.yml.j2 +1 -0
- sky/templates/do-ray.yml.j2 +2 -1
- sky/templates/fluidstack-ray.yml.j2 +1 -0
- sky/templates/gcp-ray.yml.j2 +62 -1
- sky/templates/hyperbolic-ray.yml.j2 +68 -0
- sky/templates/ibm-ray.yml.j2 +2 -1
- sky/templates/jobs-controller.yaml.j2 +27 -24
- sky/templates/kubernetes-loadbalancer.yml.j2 +2 -0
- sky/templates/kubernetes-ray.yml.j2 +611 -50
- sky/templates/lambda-ray.yml.j2 +2 -1
- sky/templates/nebius-ray.yml.j2 +34 -12
- sky/templates/oci-ray.yml.j2 +1 -0
- sky/templates/paperspace-ray.yml.j2 +2 -1
- sky/templates/primeintellect-ray.yml.j2 +72 -0
- sky/templates/runpod-ray.yml.j2 +10 -1
- sky/templates/scp-ray.yml.j2 +4 -50
- sky/templates/seeweb-ray.yml.j2 +171 -0
- sky/templates/shadeform-ray.yml.j2 +73 -0
- sky/templates/sky-serve-controller.yaml.j2 +22 -2
- sky/templates/vast-ray.yml.j2 +1 -0
- sky/templates/vsphere-ray.yml.j2 +1 -0
- sky/templates/websocket_proxy.py +212 -37
- sky/usage/usage_lib.py +31 -15
- sky/users/__init__.py +0 -0
- sky/users/model.conf +15 -0
- sky/users/permission.py +397 -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 +35 -5
- sky/utils/admin_policy_utils.py +84 -38
- sky/utils/annotations.py +38 -5
- sky/utils/asyncio_utils.py +78 -0
- sky/utils/atomic.py +1 -1
- 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 +314 -91
- sky/utils/config_utils.py +74 -5
- sky/utils/context.py +403 -0
- sky/utils/context_utils.py +242 -0
- sky/utils/controller_utils.py +383 -89
- sky/utils/dag_utils.py +31 -12
- sky/utils/db/__init__.py +0 -0
- sky/utils/db/db_utils.py +485 -0
- sky/utils/db/kv_cache.py +149 -0
- sky/utils/db/migration_utils.py +137 -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 +15 -29
- sky/utils/kubernetes/delete_cluster.sh +10 -7
- sky/utils/kubernetes/deploy_ssh_node_pools.py +1177 -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 +18 -8
- sky/utils/kubernetes/k8s_gpu_labeler_job.yaml +2 -1
- sky/utils/kubernetes/k8s_gpu_labeler_setup.yaml +16 -16
- sky/utils/kubernetes/kubernetes_deploy_utils.py +284 -114
- sky/utils/kubernetes/rsync_helper.sh +11 -3
- 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 +416 -0
- sky/utils/log_utils.py +82 -107
- 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 +217 -39
- sky/utils/schemas.py +955 -160
- sky/utils/serialize_utils.py +16 -0
- sky/utils/status_lib.py +10 -0
- sky/utils/subprocess_utils.py +29 -15
- sky/utils/tempstore.py +70 -0
- sky/utils/thread_utils.py +91 -0
- sky/utils/timeline.py +26 -53
- sky/utils/ux_utils.py +84 -15
- sky/utils/validator.py +11 -1
- sky/utils/volume.py +165 -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 +150 -0
- sky/volumes/server/__init__.py +0 -0
- sky/volumes/server/core.py +270 -0
- sky/volumes/server/server.py +124 -0
- sky/volumes/volume.py +215 -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
- sky_templates/README.md +3 -0
- sky_templates/__init__.py +3 -0
- sky_templates/ray/__init__.py +0 -0
- sky_templates/ray/start_cluster +183 -0
- sky_templates/ray/stop_cluster +75 -0
- skypilot_nightly-1.0.0.dev20251203.dist-info/METADATA +676 -0
- skypilot_nightly-1.0.0.dev20251203.dist-info/RECORD +611 -0
- {skypilot_nightly-1.0.0.dev20250502.dist-info → skypilot_nightly-1.0.0.dev20251203.dist-info}/WHEEL +1 -1
- skypilot_nightly-1.0.0.dev20251203.dist-info/top_level.txt +2 -0
- 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/GWvVBSCS7FmUiVmjaL1a7/_buildManifest.js +0 -1
- sky/dashboard/out/_next/static/chunks/236-2db3ee3fba33dd9e.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-9e60713e0c441abc.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]-6ac338bc2239cb45.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]-1c519e1afc523dc9.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.dev20250502.dist-info/METADATA +0 -361
- skypilot_nightly-1.0.0.dev20250502.dist-info/RECORD +0 -396
- skypilot_nightly-1.0.0.dev20250502.dist-info/top_level.txt +0 -1
- /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/{GWvVBSCS7FmUiVmjaL1a7 → 96_E2yl3QAiIJGOYCkSpB}/_ssgManifest.js +0 -0
- {skypilot_nightly-1.0.0.dev20250502.dist-info → skypilot_nightly-1.0.0.dev20251203.dist-info}/entry_points.txt +0 -0
- {skypilot_nightly-1.0.0.dev20250502.dist-info → skypilot_nightly-1.0.0.dev20251203.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"""Script to fetch Hyperbolic instance data and generate catalog."""
|
|
2
|
+
import argparse
|
|
3
|
+
import csv
|
|
4
|
+
import json
|
|
5
|
+
import os
|
|
6
|
+
import sys
|
|
7
|
+
from typing import Any, Dict
|
|
8
|
+
|
|
9
|
+
import requests
|
|
10
|
+
|
|
11
|
+
ENDPOINT = 'https://api.hyperbolic.xyz/v2/skypilot/catalog'
|
|
12
|
+
API_KEY_PATH = os.path.expanduser('~/.hyperbolic/api_key')
|
|
13
|
+
|
|
14
|
+
REQUIRED_FIELDS = [
|
|
15
|
+
'InstanceType', 'AcceleratorName', 'AcceleratorCount', 'vCPUs', 'MemoryGiB',
|
|
16
|
+
'StorageGiB', 'Price', 'Region', 'GpuInfo', 'SpotPrice'
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class HyperbolicCatalogError(Exception):
|
|
21
|
+
"""Base exception for Hyperbolic catalog errors."""
|
|
22
|
+
pass
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def get_api_key(api_key=None) -> str:
|
|
26
|
+
"""Get API key from arg, env var, or file."""
|
|
27
|
+
if api_key:
|
|
28
|
+
return api_key
|
|
29
|
+
if api_key := os.environ.get('HYPERBOLIC_API_KEY'):
|
|
30
|
+
return api_key
|
|
31
|
+
try:
|
|
32
|
+
with open(API_KEY_PATH, 'r', encoding='utf-8') as f:
|
|
33
|
+
return f.read().strip()
|
|
34
|
+
except FileNotFoundError as exc:
|
|
35
|
+
raise HyperbolicCatalogError(
|
|
36
|
+
'No API key found. Please either:\n'
|
|
37
|
+
'1. Pass --api-key\n'
|
|
38
|
+
'2. Set HYPERBOLIC_API_KEY environment variable\n'
|
|
39
|
+
'3. Create ~/.hyperbolic/api_key file') from exc
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def get_output_path() -> str:
|
|
43
|
+
"""Get output path for catalog file."""
|
|
44
|
+
current_dir = os.getcwd()
|
|
45
|
+
if os.path.basename(current_dir) == 'hyperbolic':
|
|
46
|
+
return 'vms.csv'
|
|
47
|
+
hyperbolic_dir = os.path.join(current_dir, 'hyperbolic')
|
|
48
|
+
os.makedirs(hyperbolic_dir, exist_ok=True)
|
|
49
|
+
return os.path.join(hyperbolic_dir, 'vms.csv')
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def validate_instance_data(instance: Dict[str, Any]) -> None:
|
|
53
|
+
"""Validate instance data has all required fields."""
|
|
54
|
+
missing_fields = [
|
|
55
|
+
field for field in REQUIRED_FIELDS if field not in instance
|
|
56
|
+
]
|
|
57
|
+
if missing_fields:
|
|
58
|
+
raise HyperbolicCatalogError(
|
|
59
|
+
f'Instance data missing required fields: {missing_fields}')
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def create_catalog(api_key=None) -> None:
|
|
63
|
+
"""Generate Hyperbolic catalog CSV file."""
|
|
64
|
+
try:
|
|
65
|
+
response = requests.get(
|
|
66
|
+
ENDPOINT,
|
|
67
|
+
headers={'Authorization': f'Bearer {get_api_key(api_key)}'},
|
|
68
|
+
timeout=30)
|
|
69
|
+
response.raise_for_status()
|
|
70
|
+
|
|
71
|
+
try:
|
|
72
|
+
data = response.json()
|
|
73
|
+
except json.JSONDecodeError as e:
|
|
74
|
+
raise HyperbolicCatalogError(
|
|
75
|
+
f'Invalid JSON response from API: {response.text}') from e
|
|
76
|
+
|
|
77
|
+
if 'vms' not in data:
|
|
78
|
+
raise HyperbolicCatalogError(
|
|
79
|
+
f'Missing "vms" field in API response: {data}')
|
|
80
|
+
|
|
81
|
+
instances = data['vms']
|
|
82
|
+
if not isinstance(instances, list):
|
|
83
|
+
raise HyperbolicCatalogError(
|
|
84
|
+
f'Expected list of instances, got {type(instances)}')
|
|
85
|
+
|
|
86
|
+
if not instances:
|
|
87
|
+
raise HyperbolicCatalogError('No instances found in API response')
|
|
88
|
+
|
|
89
|
+
# Validate each instance
|
|
90
|
+
for instance in instances:
|
|
91
|
+
validate_instance_data(instance)
|
|
92
|
+
|
|
93
|
+
except requests.exceptions.RequestException as e:
|
|
94
|
+
raise HyperbolicCatalogError(
|
|
95
|
+
f'Failed to fetch instance data: {e}') from e
|
|
96
|
+
|
|
97
|
+
output_path = get_output_path()
|
|
98
|
+
try:
|
|
99
|
+
with open(output_path, 'w', newline='', encoding='utf-8') as f:
|
|
100
|
+
writer = csv.DictWriter(f, fieldnames=REQUIRED_FIELDS)
|
|
101
|
+
writer.writeheader()
|
|
102
|
+
|
|
103
|
+
for instance in instances:
|
|
104
|
+
entry = instance.copy()
|
|
105
|
+
# Convert GpuInfo to string format
|
|
106
|
+
entry['GpuInfo'] = json.dumps(entry['GpuInfo'],
|
|
107
|
+
ensure_ascii=False).replace(
|
|
108
|
+
'"', "'") # pylint: disable=invalid-string-quote
|
|
109
|
+
writer.writerow(entry)
|
|
110
|
+
except (IOError, OSError) as e:
|
|
111
|
+
raise HyperbolicCatalogError(
|
|
112
|
+
f'Failed to write catalog file to {output_path}: {e}') from e
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def main() -> int:
|
|
116
|
+
"""Main entry point."""
|
|
117
|
+
parser = argparse.ArgumentParser(
|
|
118
|
+
description='Fetch Hyperbolic instance data')
|
|
119
|
+
parser.add_argument('--api-key', help='Hyperbolic API key')
|
|
120
|
+
args = parser.parse_args()
|
|
121
|
+
|
|
122
|
+
try:
|
|
123
|
+
create_catalog(args.api_key)
|
|
124
|
+
print(f'Hyperbolic Service Catalog saved to {get_output_path()}')
|
|
125
|
+
return 0
|
|
126
|
+
except HyperbolicCatalogError as e:
|
|
127
|
+
print(f'Error: {e}', file=sys.stderr)
|
|
128
|
+
return 1
|
|
129
|
+
except (requests.exceptions.RequestException, json.JSONDecodeError, IOError,
|
|
130
|
+
OSError) as e:
|
|
131
|
+
print(f'Unexpected error: {e}', file=sys.stderr)
|
|
132
|
+
return 1
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
if __name__ == '__main__':
|
|
136
|
+
sys.exit(main())
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
"""A script that queries Nebius API to get instance types and pricing info.
|
|
2
|
+
|
|
3
|
+
This script takes about 1 minute to finish.
|
|
4
|
+
"""
|
|
5
|
+
import csv
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
import decimal
|
|
8
|
+
import json
|
|
9
|
+
import logging
|
|
10
|
+
import os
|
|
11
|
+
import re
|
|
12
|
+
from typing import Any, Dict, List, Optional
|
|
13
|
+
|
|
14
|
+
from sky.adaptors import nebius
|
|
15
|
+
from sky.adaptors.nebius import billing
|
|
16
|
+
from sky.adaptors.nebius import compute
|
|
17
|
+
from sky.adaptors.nebius import iam
|
|
18
|
+
from sky.adaptors.nebius import nebius_common
|
|
19
|
+
|
|
20
|
+
logger = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
TIMEOUT = 10
|
|
23
|
+
PARENT_ID_TEMPLATE = 'project-{}public-images'
|
|
24
|
+
ACCELERATOR_MANUFACTURER = 'NVIDIA'
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass
|
|
28
|
+
class PresetInfo:
|
|
29
|
+
"""Represents information about a specific compute preset,
|
|
30
|
+
including its pricing.
|
|
31
|
+
|
|
32
|
+
Attributes:
|
|
33
|
+
region (str): The geographical region where the preset is available.
|
|
34
|
+
fullname (str): The full name of the preset, a combination of platform
|
|
35
|
+
and preset name.
|
|
36
|
+
name (str): The name of the preset.
|
|
37
|
+
platform_name (str): The name of the platform the preset belongs to.
|
|
38
|
+
gpu (int): The number of GPUs in the preset.
|
|
39
|
+
vcpu (int): The number of virtual CPUs in the preset.
|
|
40
|
+
gpu_memory_gibibytes (int): size of gpu memory in GiB.
|
|
41
|
+
memory_gib (int): The amount of memory in GiB in the preset.
|
|
42
|
+
accelerator_manufacturer (str | None): The manufacturer of the
|
|
43
|
+
accelerator (e.g., "NVIDIA"), or None if no accelerator.
|
|
44
|
+
accelerator_name (str | None): The name of the accelerator
|
|
45
|
+
(e.g., "H100"), or None if no accelerator.
|
|
46
|
+
price_hourly (decimal.Decimal): The hourly price of the preset.
|
|
47
|
+
spot_price (decimal.Decimal): The spot (preemptible) price
|
|
48
|
+
of the preset.
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
region: str
|
|
52
|
+
fullname: str
|
|
53
|
+
name: str
|
|
54
|
+
platform_name: str
|
|
55
|
+
gpu: int
|
|
56
|
+
vcpu: int
|
|
57
|
+
gpu_memory_gibibytes: int
|
|
58
|
+
memory_gib: int
|
|
59
|
+
accelerator_manufacturer: Optional[str]
|
|
60
|
+
accelerator_name: Optional[str]
|
|
61
|
+
price_hourly: decimal.Decimal
|
|
62
|
+
spot_price: decimal.Decimal
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _format_decimal(value: decimal.Decimal) -> str:
|
|
66
|
+
"""Formats a decimal value to a string with at least two decimal places,
|
|
67
|
+
removing trailing zeros and ensuring a two-digit decimal part.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
value (decimal.Decimal): The decimal value to format.
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
str: The formatted string representation of the decimal.
|
|
74
|
+
"""
|
|
75
|
+
formatted_value = f'{value:f}'
|
|
76
|
+
integer_part, decimal_part = formatted_value.split(
|
|
77
|
+
'.') if '.' in formatted_value else (formatted_value, '')
|
|
78
|
+
if len(decimal_part) < 2:
|
|
79
|
+
decimal_part += '0' * (2 - len(decimal_part))
|
|
80
|
+
|
|
81
|
+
return f'{integer_part}.{decimal_part}'
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _estimate_platforms(platforms: List[Any], parent_id: str,
|
|
85
|
+
region: str) -> List[PresetInfo]:
|
|
86
|
+
"""Collects specifications for all presets on the given platforms to form a
|
|
87
|
+
batch price request. It then sends the request and processes the responses
|
|
88
|
+
to create a list of PresetInfo objects.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
platforms (List[Platform]): A List of compute platforms to estimate
|
|
92
|
+
prices for.
|
|
93
|
+
parent_id (str): The parent ID used for resource metadata
|
|
94
|
+
in the estimate request.
|
|
95
|
+
region (str): The region associated with the platforms.
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
List[PresetInfo]: A list of PresetInfo objects containing details and
|
|
99
|
+
estimated prices for each preset.
|
|
100
|
+
"""
|
|
101
|
+
|
|
102
|
+
calculator_service = billing().CalculatorServiceClient(nebius.sdk())
|
|
103
|
+
futures = []
|
|
104
|
+
|
|
105
|
+
for platform in platforms:
|
|
106
|
+
platform_name = platform.metadata.name
|
|
107
|
+
|
|
108
|
+
for preset in platform.spec.presets:
|
|
109
|
+
# Form the specification for the price request
|
|
110
|
+
estimate_spec = billing().ResourceSpec(
|
|
111
|
+
compute_instance_spec=compute().CreateInstanceRequest(
|
|
112
|
+
metadata=nebius_common().ResourceMetadata(
|
|
113
|
+
parent_id=parent_id,),
|
|
114
|
+
spec=compute().InstanceSpec(
|
|
115
|
+
resources=compute().ResourcesSpec(
|
|
116
|
+
platform=platform_name,
|
|
117
|
+
preset=preset.name,
|
|
118
|
+
)),
|
|
119
|
+
))
|
|
120
|
+
price_request = billing().EstimateBatchRequest(
|
|
121
|
+
resource_specs=[estimate_spec])
|
|
122
|
+
|
|
123
|
+
# Form the specification for the spot price request
|
|
124
|
+
spot_estimate_spec = billing().ResourceSpec(
|
|
125
|
+
compute_instance_spec=compute().CreateInstanceRequest(
|
|
126
|
+
metadata=nebius_common().ResourceMetadata(
|
|
127
|
+
parent_id=parent_id,),
|
|
128
|
+
spec=compute().InstanceSpec(
|
|
129
|
+
resources=compute().ResourcesSpec(
|
|
130
|
+
platform=platform_name,
|
|
131
|
+
preset=preset.name,
|
|
132
|
+
),
|
|
133
|
+
preemptible=compute().PreemptibleSpec(priority=1),
|
|
134
|
+
),
|
|
135
|
+
))
|
|
136
|
+
spot_price_request = billing().EstimateBatchRequest(
|
|
137
|
+
resource_specs=[spot_estimate_spec])
|
|
138
|
+
|
|
139
|
+
# Start future for each preset
|
|
140
|
+
futures.append((
|
|
141
|
+
platform,
|
|
142
|
+
preset,
|
|
143
|
+
calculator_service.estimate_batch(price_request,
|
|
144
|
+
timeout=TIMEOUT),
|
|
145
|
+
calculator_service.estimate_batch(spot_price_request,
|
|
146
|
+
timeout=TIMEOUT),
|
|
147
|
+
))
|
|
148
|
+
|
|
149
|
+
# wait all futures to complete and collect results
|
|
150
|
+
result = []
|
|
151
|
+
for platform, preset, future, future_spot in futures:
|
|
152
|
+
platform_name = platform.metadata.name
|
|
153
|
+
result.append(
|
|
154
|
+
PresetInfo(
|
|
155
|
+
region=region,
|
|
156
|
+
fullname=f'{platform_name}_{preset.name}',
|
|
157
|
+
name=preset.name,
|
|
158
|
+
platform_name=platform_name,
|
|
159
|
+
gpu=preset.resources.gpu_count or 0,
|
|
160
|
+
vcpu=preset.resources.vcpu_count,
|
|
161
|
+
gpu_memory_gibibytes=platform.spec.gpu_memory_gibibytes,
|
|
162
|
+
memory_gib=preset.resources.memory_gibibytes,
|
|
163
|
+
accelerator_manufacturer=ACCELERATOR_MANUFACTURER
|
|
164
|
+
if platform_name.startswith('gpu-') else '',
|
|
165
|
+
accelerator_name=platform_name.split('-')[1].upper()
|
|
166
|
+
if platform_name.startswith('gpu-') else '',
|
|
167
|
+
price_hourly=decimal.Decimal(
|
|
168
|
+
future.wait().hourly_cost.general.total.cost),
|
|
169
|
+
spot_price=decimal.Decimal(
|
|
170
|
+
future_spot.wait().hourly_cost.general.total.cost),
|
|
171
|
+
))
|
|
172
|
+
|
|
173
|
+
return result
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def _write_preset_prices(presets: List[PresetInfo], output_file: str) -> None:
|
|
177
|
+
"""Writes the provided preset information to a CSV file.
|
|
178
|
+
|
|
179
|
+
Args:
|
|
180
|
+
presets (List[PresetInfo]): A list of PresetInfo objects to write.
|
|
181
|
+
output_file (str): The path to the output CSV file.
|
|
182
|
+
"""
|
|
183
|
+
os.makedirs(os.path.dirname(output_file))
|
|
184
|
+
# Set up the CSV writer to output to stdout
|
|
185
|
+
with open(output_file, 'w', encoding='utf-8') as out:
|
|
186
|
+
header = [
|
|
187
|
+
'InstanceType',
|
|
188
|
+
'AcceleratorName',
|
|
189
|
+
'AcceleratorCount',
|
|
190
|
+
'vCPUs',
|
|
191
|
+
'MemoryGiB',
|
|
192
|
+
'Price',
|
|
193
|
+
'Region',
|
|
194
|
+
'GpuInfo',
|
|
195
|
+
'SpotPrice',
|
|
196
|
+
]
|
|
197
|
+
writer = csv.DictWriter(out, fieldnames=header)
|
|
198
|
+
writer.writeheader()
|
|
199
|
+
# logger.info(presets)
|
|
200
|
+
for preset in sorted(presets,
|
|
201
|
+
key=lambda x:
|
|
202
|
+
(bool(x.gpu), x.region, x.platform_name, x.vcpu)):
|
|
203
|
+
gpu_info = ''
|
|
204
|
+
if preset.gpu > 0 and preset.accelerator_name:
|
|
205
|
+
vram = preset.gpu_memory_gibibytes * 1024
|
|
206
|
+
gpu_info_dict = {
|
|
207
|
+
'Gpus': [{
|
|
208
|
+
'Name': preset.accelerator_name,
|
|
209
|
+
'Manufacturer': preset.accelerator_manufacturer,
|
|
210
|
+
'Count': preset.gpu,
|
|
211
|
+
'MemoryInfo': {
|
|
212
|
+
'SizeInMiB': vram
|
|
213
|
+
},
|
|
214
|
+
}],
|
|
215
|
+
'TotalGpuMemoryInMiB': vram * preset.gpu,
|
|
216
|
+
}
|
|
217
|
+
gpu_info = json.dumps(gpu_info_dict).replace('"', '\'')
|
|
218
|
+
|
|
219
|
+
writer.writerow({
|
|
220
|
+
'InstanceType': preset.fullname,
|
|
221
|
+
'AcceleratorName': preset.accelerator_name,
|
|
222
|
+
'AcceleratorCount': preset.gpu,
|
|
223
|
+
'vCPUs': preset.vcpu,
|
|
224
|
+
'MemoryGiB': preset.memory_gib,
|
|
225
|
+
'Price': _format_decimal(preset.price_hourly),
|
|
226
|
+
'Region': preset.region,
|
|
227
|
+
'GpuInfo': gpu_info,
|
|
228
|
+
'SpotPrice': _format_decimal(preset.spot_price)
|
|
229
|
+
if preset.spot_price else '',
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def _fetch_platforms_for_project(project_id: str) -> List[Any]:
|
|
234
|
+
"""Fetches all available compute platforms for a given project.
|
|
235
|
+
|
|
236
|
+
Args:
|
|
237
|
+
project_id (str): The ID of the project to fetch platforms from.
|
|
238
|
+
|
|
239
|
+
Returns:
|
|
240
|
+
List[ComputePlatform]: A list of ComputePlatform objects available
|
|
241
|
+
in the project.
|
|
242
|
+
"""
|
|
243
|
+
platform_service = compute().PlatformServiceClient(nebius.sdk())
|
|
244
|
+
|
|
245
|
+
platform_request = compute().ListPlatformsRequest(page_size=999,
|
|
246
|
+
parent_id=project_id)
|
|
247
|
+
platform_response = platform_service.list(platform_request,
|
|
248
|
+
timeout=TIMEOUT).wait()
|
|
249
|
+
|
|
250
|
+
return platform_response.items
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def _get_regions_map() -> Dict[str, str]:
|
|
254
|
+
"""Maps region codes to their full names by iterating through tenants and
|
|
255
|
+
projects.
|
|
256
|
+
|
|
257
|
+
Returns:
|
|
258
|
+
dict[str, str]: A dictionary where keys are region codes (e.g., "e00")
|
|
259
|
+
and values are full region names (e.g., "eu-north1").
|
|
260
|
+
"""
|
|
261
|
+
result = {}
|
|
262
|
+
response = iam().TenantServiceClient(nebius.sdk()).list(
|
|
263
|
+
iam().ListTenantsRequest(), timeout=TIMEOUT).wait()
|
|
264
|
+
|
|
265
|
+
for tenant in response.items:
|
|
266
|
+
projects = (iam().ProjectServiceClient(nebius.sdk()).list(
|
|
267
|
+
iam().ListProjectsRequest(parent_id=tenant.metadata.id),
|
|
268
|
+
timeout=TIMEOUT).wait())
|
|
269
|
+
|
|
270
|
+
for project in projects.items:
|
|
271
|
+
match = re.match(r'^project-([a-z0-9]{3})', project.metadata.id)
|
|
272
|
+
if match is None:
|
|
273
|
+
logger.error('Could not parse project id %s',
|
|
274
|
+
project.metadata.id)
|
|
275
|
+
continue
|
|
276
|
+
result[match.group(1)] = project.status.region
|
|
277
|
+
|
|
278
|
+
return result
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def _get_all_platform_prices() -> List[PresetInfo]:
|
|
282
|
+
"""Orchestrates fetching specifications and prices for all platforms across
|
|
283
|
+
all regions.
|
|
284
|
+
|
|
285
|
+
This function first retrieves a map of region codes to full names, then
|
|
286
|
+
iterates through each region, fetches available platforms for
|
|
287
|
+
the corresponding project ID, and finally estimates prices for all presets
|
|
288
|
+
on those platforms.
|
|
289
|
+
|
|
290
|
+
Returns:
|
|
291
|
+
List[PresetInfo]: A consolidated list of PresetInfo objects for all
|
|
292
|
+
platforms and presets across all regions.
|
|
293
|
+
"""
|
|
294
|
+
|
|
295
|
+
# Get regions codes to names
|
|
296
|
+
regions_map = _get_regions_map()
|
|
297
|
+
|
|
298
|
+
presets = []
|
|
299
|
+
|
|
300
|
+
for region_code in sorted(regions_map.keys()):
|
|
301
|
+
project_id = PARENT_ID_TEMPLATE.format(region_code)
|
|
302
|
+
region = regions_map[region_code]
|
|
303
|
+
logger.info('Processing region: %s (project: %s)...', region,
|
|
304
|
+
project_id)
|
|
305
|
+
|
|
306
|
+
platforms = _fetch_platforms_for_project(project_id)
|
|
307
|
+
if not platforms:
|
|
308
|
+
logger.warning('No platforms found in region %s', region)
|
|
309
|
+
continue
|
|
310
|
+
|
|
311
|
+
presets.extend(
|
|
312
|
+
_estimate_platforms(platforms=platforms,
|
|
313
|
+
parent_id=project_id,
|
|
314
|
+
region=region))
|
|
315
|
+
|
|
316
|
+
return presets
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def main() -> None:
|
|
320
|
+
"""Main function to fetch and write Nebius platform prices to a CSV file.
|
|
321
|
+
|
|
322
|
+
It initializes the SDK, fetches all platform prices, and then writes them
|
|
323
|
+
to the specified CSV file.
|
|
324
|
+
"""
|
|
325
|
+
|
|
326
|
+
output_file = 'nebius/vms.csv'
|
|
327
|
+
|
|
328
|
+
# Fetch presets and estimate
|
|
329
|
+
presets = _get_all_platform_prices()
|
|
330
|
+
|
|
331
|
+
# Write CSV
|
|
332
|
+
_write_preset_prices(presets, output_file)
|
|
333
|
+
|
|
334
|
+
logger.info('Done!')
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
if __name__ == '__main__':
|
|
338
|
+
main()
|