skypilot-nightly 1.0.0.dev20250627__py3-none-any.whl → 1.0.0.dev20250630__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 +2 -2
- sky/adaptors/kubernetes.py +14 -0
- sky/adaptors/nebius.py +2 -2
- sky/authentication.py +12 -5
- sky/backends/backend_utils.py +92 -26
- sky/check.py +5 -2
- sky/client/cli/command.py +39 -8
- sky/client/sdk.py +217 -167
- sky/client/service_account_auth.py +47 -0
- sky/clouds/aws.py +10 -4
- sky/clouds/azure.py +5 -2
- sky/clouds/cloud.py +5 -2
- sky/clouds/gcp.py +31 -18
- sky/clouds/kubernetes.py +54 -34
- sky/clouds/nebius.py +8 -2
- sky/clouds/ssh.py +5 -2
- sky/clouds/utils/aws_utils.py +10 -4
- sky/clouds/utils/gcp_utils.py +22 -7
- sky/clouds/utils/oci_utils.py +62 -14
- sky/dashboard/out/404.html +1 -1
- sky/dashboard/out/_next/static/NdypbqMxaYucRGfopkKXa/_buildManifest.js +1 -0
- sky/dashboard/out/_next/static/chunks/1043-1b39779691bb4030.js +1 -0
- sky/dashboard/out/_next/static/chunks/{141-fa5a20cbf401b351.js → 1141-726e5a3f00b67185.js} +2 -2
- sky/dashboard/out/_next/static/chunks/1272-1ef0bf0237faccdb.js +1 -0
- sky/dashboard/out/_next/static/chunks/1664-d65361e92b85e786.js +1 -0
- sky/dashboard/out/_next/static/chunks/1691.44e378727a41f3b5.js +21 -0
- sky/dashboard/out/_next/static/chunks/1871-80dea41717729fa5.js +6 -0
- sky/dashboard/out/_next/static/chunks/2544.27f70672535675ed.js +1 -0
- sky/dashboard/out/_next/static/chunks/{875.52c962183328b3f2.js → 2875.c24c6d57dc82e436.js} +1 -1
- sky/dashboard/out/_next/static/chunks/3256.7257acd01b481bed.js +11 -0
- sky/dashboard/out/_next/static/chunks/3698-52ad1ca228faa776.js +1 -0
- sky/dashboard/out/_next/static/chunks/3785.b3cc2bc1d49d2c3c.js +1 -0
- sky/dashboard/out/_next/static/chunks/3937.d7f1c55d1916c7f2.js +1 -0
- sky/dashboard/out/_next/static/chunks/{947-6620842ef80ae879.js → 3947-b059261d6fa88a1f.js} +1 -1
- sky/dashboard/out/_next/static/chunks/{697.6460bf72e760addd.js → 4697.f5421144224da9fc.js} +1 -1
- sky/dashboard/out/_next/static/chunks/4725.4c849b1e05c8e9ad.js +1 -0
- sky/dashboard/out/_next/static/chunks/5230-df791914b54d91d9.js +1 -0
- sky/dashboard/out/_next/static/chunks/{491.b3d264269613fe09.js → 5491.918ffed0ba7a5294.js} +1 -1
- sky/dashboard/out/_next/static/chunks/5739-5ea3ffa10fc884f2.js +8 -0
- sky/dashboard/out/_next/static/chunks/616-162f3033ffcd3d31.js +39 -0
- sky/dashboard/out/_next/static/chunks/6601-fcfad0ddf92ec7ab.js +1 -0
- sky/dashboard/out/_next/static/chunks/6989-6ff4e45dfb49d11d.js +1 -0
- sky/dashboard/out/_next/static/chunks/6990-d0dc765474fa0eca.js +1 -0
- sky/dashboard/out/_next/static/chunks/8969-909d53833da080cb.js +1 -0
- sky/dashboard/out/_next/static/chunks/8982.a2e214068f30a857.js +1 -0
- sky/dashboard/out/_next/static/chunks/{25.76c246239df93d50.js → 9025.a7c44babfe56ce09.js} +2 -2
- sky/dashboard/out/_next/static/chunks/938-044ad21de8b4626b.js +1 -0
- sky/dashboard/out/_next/static/chunks/9470-21d059a1dfa03f61.js +1 -0
- sky/dashboard/out/_next/static/chunks/9984.739ae958a066298d.js +1 -0
- sky/dashboard/out/_next/static/chunks/fd9d1056-61f2257a9cd8b32b.js +1 -0
- sky/dashboard/out/_next/static/chunks/{framework-87d061ee6ed71b28.js → framework-efc06c2733009cd3.js} +1 -1
- sky/dashboard/out/_next/static/chunks/main-app-68c028b1bc5e1b72.js +1 -0
- sky/dashboard/out/_next/static/chunks/{main-e0e2335212e72357.js → main-c0a4f1ea606d48d2.js} +1 -1
- sky/dashboard/out/_next/static/chunks/pages/{_app-9a3ce3170d2edcec.js → _app-a37b06ddb64521fd.js} +2 -2
- sky/dashboard/out/_next/static/chunks/pages/_error-c72a1f77a3c0be1b.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]/[job]-8135aba0712bda37.js +6 -0
- sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]-b8e1114e6d38218c.js +6 -0
- sky/dashboard/out/_next/static/chunks/pages/clusters-9744c271a1642f76.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/config-a2673b256b6d416f.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/index-927ddeebe57a8ac3.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/infra/[context]-8b0809f59034d509.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/infra-ae9d2f705ce582c9.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/jobs/[job]-c4d5cfac7fbc0668.js +16 -0
- sky/dashboard/out/_next/static/chunks/pages/jobs-5bbdc71878f0a068.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/users-cd43fb3c122eedde.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/volumes-4ebf6484f7216387.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/workspace/new-5629d4e551dba1ee.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/workspaces/[name]-7c0187f43757a548.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/workspaces-06bde99155fa6292.js +1 -0
- sky/dashboard/out/_next/static/chunks/webpack-d427db53e54de9ce.js +1 -0
- sky/dashboard/out/_next/static/css/0da6afe66176678a.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 -1
- sky/dashboard/out/index.html +1 -1
- sky/dashboard/out/infra/[context].html +1 -1
- sky/dashboard/out/infra.html +1 -1
- sky/dashboard/out/jobs/[job].html +1 -1
- sky/dashboard/out/jobs.html +1 -1
- sky/dashboard/out/users.html +1 -1
- sky/dashboard/out/volumes.html +1 -1
- sky/dashboard/out/workspace/new.html +1 -1
- sky/dashboard/out/workspaces/[name].html +1 -1
- sky/dashboard/out/workspaces.html +1 -1
- sky/data/storage.py +8 -3
- sky/global_user_state.py +257 -9
- sky/jobs/client/sdk.py +20 -25
- sky/models.py +16 -0
- sky/optimizer.py +46 -0
- sky/provision/__init__.py +14 -6
- sky/provision/kubernetes/config.py +1 -1
- sky/provision/kubernetes/constants.py +9 -0
- sky/provision/kubernetes/instance.py +24 -18
- sky/provision/kubernetes/network.py +15 -9
- sky/provision/kubernetes/network_utils.py +42 -23
- sky/provision/kubernetes/utils.py +73 -35
- sky/provision/kubernetes/volume.py +77 -15
- sky/provision/nebius/utils.py +10 -4
- sky/resources.py +10 -4
- sky/serve/client/sdk.py +28 -34
- sky/server/common.py +51 -3
- sky/server/constants.py +3 -0
- sky/server/requests/executor.py +4 -0
- sky/server/requests/payloads.py +33 -0
- sky/server/requests/requests.py +19 -0
- sky/server/rest.py +6 -15
- sky/server/server.py +121 -6
- sky/skylet/constants.py +7 -0
- sky/skypilot_config.py +32 -4
- sky/task.py +12 -0
- sky/users/permission.py +29 -0
- sky/users/server.py +384 -5
- sky/users/token_service.py +196 -0
- sky/utils/common_utils.py +4 -5
- sky/utils/config_utils.py +41 -0
- sky/utils/controller_utils.py +5 -1
- sky/utils/log_utils.py +68 -0
- sky/utils/resource_checker.py +153 -0
- sky/utils/resources_utils.py +12 -4
- sky/utils/schemas.py +87 -60
- sky/utils/subprocess_utils.py +2 -6
- sky/volumes/server/core.py +103 -78
- sky/volumes/utils.py +22 -5
- sky/workspaces/core.py +9 -117
- {skypilot_nightly-1.0.0.dev20250627.dist-info → skypilot_nightly-1.0.0.dev20250630.dist-info}/METADATA +1 -1
- {skypilot_nightly-1.0.0.dev20250627.dist-info → skypilot_nightly-1.0.0.dev20250630.dist-info}/RECORD +133 -128
- sky/dashboard/out/_next/static/HudU4f4Xsy-cP51JvXSZ-/_buildManifest.js +0 -1
- sky/dashboard/out/_next/static/chunks/230-d6e363362017ff3a.js +0 -1
- sky/dashboard/out/_next/static/chunks/43-36177d00f6956ab2.js +0 -1
- sky/dashboard/out/_next/static/chunks/470-92dd1614396389be.js +0 -1
- sky/dashboard/out/_next/static/chunks/544.110e53813fb98e2e.js +0 -1
- sky/dashboard/out/_next/static/chunks/616-d6128fa9e7cae6e6.js +0 -39
- sky/dashboard/out/_next/static/chunks/645.961f08e39b8ce447.js +0 -1
- sky/dashboard/out/_next/static/chunks/664-047bc03493fda379.js +0 -1
- sky/dashboard/out/_next/static/chunks/690.55f9eed3be903f56.js +0 -16
- sky/dashboard/out/_next/static/chunks/785.dc2686c3c1235554.js +0 -1
- sky/dashboard/out/_next/static/chunks/798-c0525dc3f21e488d.js +0 -1
- sky/dashboard/out/_next/static/chunks/799-3625946b2ec2eb30.js +0 -8
- sky/dashboard/out/_next/static/chunks/871-3db673be3ee3750b.js +0 -6
- sky/dashboard/out/_next/static/chunks/937.3759f538f11a0953.js +0 -1
- sky/dashboard/out/_next/static/chunks/938-068520cc11738deb.js +0 -1
- sky/dashboard/out/_next/static/chunks/969-d3a0b53f728d280a.js +0 -1
- sky/dashboard/out/_next/static/chunks/973-81b2d057178adb76.js +0 -1
- sky/dashboard/out/_next/static/chunks/982.1b61658204416b0f.js +0 -1
- sky/dashboard/out/_next/static/chunks/984.e8bac186a24e5178.js +0 -1
- sky/dashboard/out/_next/static/chunks/989-db34c16ad7ea6155.js +0 -1
- sky/dashboard/out/_next/static/chunks/990-0ad5ea1699e03ee8.js +0 -1
- sky/dashboard/out/_next/static/chunks/fd9d1056-2821b0f0cabcd8bd.js +0 -1
- sky/dashboard/out/_next/static/chunks/main-app-241eb28595532291.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]-aff040d7bc5d0086.js +0 -6
- sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]-8040f2483897ed0c.js +0 -6
- sky/dashboard/out/_next/static/chunks/pages/clusters-f119a5630a1efd61.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/config-6b255eae088da6a3.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/index-6b0d9e5031b70c58.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/infra/[context]-b302aea4d65766bf.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/infra-ee8cc4d449945d19.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/jobs/[job]-e4b23128db0774cd.js +0 -16
- sky/dashboard/out/_next/static/chunks/pages/jobs-0a5695ff3075d94a.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/users-4978cbb093e141e7.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/volumes-476b670ef33d1ecd.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/workspace/new-5b59bce9eb208d84.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/workspaces/[name]-cb7e720b739de53a.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/workspaces-50e230828730cfb3.js +0 -1
- sky/dashboard/out/_next/static/chunks/webpack-08fdb9e6070127fc.js +0 -1
- sky/dashboard/out/_next/static/css/52082cf558ec9705.css +0 -3
- /sky/dashboard/out/_next/static/{HudU4f4Xsy-cP51JvXSZ- → NdypbqMxaYucRGfopkKXa}/_ssgManifest.js +0 -0
- /sky/dashboard/out/_next/static/chunks/{804-4c9fc53aa74bc191.js → 804-9f5e98ce84d46bdd.js} +0 -0
- {skypilot_nightly-1.0.0.dev20250627.dist-info → skypilot_nightly-1.0.0.dev20250630.dist-info}/WHEEL +0 -0
- {skypilot_nightly-1.0.0.dev20250627.dist-info → skypilot_nightly-1.0.0.dev20250630.dist-info}/entry_points.txt +0 -0
- {skypilot_nightly-1.0.0.dev20250627.dist-info → skypilot_nightly-1.0.0.dev20250630.dist-info}/licenses/LICENSE +0 -0
- {skypilot_nightly-1.0.0.dev20250627.dist-info → skypilot_nightly-1.0.0.dev20250630.dist-info}/top_level.txt +0 -0
sky/utils/schemas.py
CHANGED
@@ -7,6 +7,7 @@ import enum
|
|
7
7
|
from typing import Any, Dict, List, Tuple
|
8
8
|
|
9
9
|
from sky.skylet import constants
|
10
|
+
from sky.utils import kubernetes_enums
|
10
11
|
|
11
12
|
|
12
13
|
def _check_not_both_fields_present(field1: str, field2: str):
|
@@ -1018,10 +1019,73 @@ _REMOTE_IDENTITY_SCHEMA_KUBERNETES = {
|
|
1018
1019
|
},
|
1019
1020
|
}
|
1020
1021
|
|
1022
|
+
_CONTEXT_CONFIG_SCHEMA_KUBERNETES = {
|
1023
|
+
'networking': {
|
1024
|
+
'type': 'string',
|
1025
|
+
'case_insensitive_enum': [
|
1026
|
+
type.value for type in kubernetes_enums.KubernetesNetworkingMode
|
1027
|
+
],
|
1028
|
+
},
|
1029
|
+
'ports': {
|
1030
|
+
'type': 'string',
|
1031
|
+
'case_insensitive_enum': [
|
1032
|
+
type.value for type in kubernetes_enums.KubernetesPortMode
|
1033
|
+
],
|
1034
|
+
},
|
1035
|
+
'pod_config': {
|
1036
|
+
'type': 'object',
|
1037
|
+
'required': [],
|
1038
|
+
# Allow arbitrary keys since validating pod spec is hard
|
1039
|
+
'additionalProperties': True,
|
1040
|
+
},
|
1041
|
+
'custom_metadata': {
|
1042
|
+
'type': 'object',
|
1043
|
+
'required': [],
|
1044
|
+
# Allow arbitrary keys since validating metadata is hard
|
1045
|
+
'additionalProperties': True,
|
1046
|
+
# Disallow 'name' and 'namespace' keys in this dict
|
1047
|
+
'not': {
|
1048
|
+
'anyOf': [{
|
1049
|
+
'required': ['name']
|
1050
|
+
}, {
|
1051
|
+
'required': ['namespace']
|
1052
|
+
}]
|
1053
|
+
},
|
1054
|
+
},
|
1055
|
+
'provision_timeout': {
|
1056
|
+
'type': 'integer',
|
1057
|
+
},
|
1058
|
+
'autoscaler': {
|
1059
|
+
'type': 'string',
|
1060
|
+
'case_insensitive_enum': [
|
1061
|
+
type.value for type in kubernetes_enums.KubernetesAutoscalerType
|
1062
|
+
],
|
1063
|
+
},
|
1064
|
+
'high_availability': {
|
1065
|
+
'type': 'object',
|
1066
|
+
'required': [],
|
1067
|
+
'additionalProperties': False,
|
1068
|
+
'properties': {
|
1069
|
+
'storage_class_name': {
|
1070
|
+
'type': 'string',
|
1071
|
+
}
|
1072
|
+
},
|
1073
|
+
},
|
1074
|
+
'kueue': {
|
1075
|
+
'type': 'object',
|
1076
|
+
'required': [],
|
1077
|
+
'additionalProperties': False,
|
1078
|
+
'properties': {
|
1079
|
+
'local_queue_name': {
|
1080
|
+
'type': 'string',
|
1081
|
+
},
|
1082
|
+
},
|
1083
|
+
},
|
1084
|
+
}
|
1085
|
+
|
1021
1086
|
|
1022
1087
|
def get_config_schema():
|
1023
1088
|
# pylint: disable=import-outside-toplevel
|
1024
|
-
from sky.utils import kubernetes_enums
|
1025
1089
|
|
1026
1090
|
resources_schema = {
|
1027
1091
|
k: v
|
@@ -1178,70 +1242,21 @@ def get_config_schema():
|
|
1178
1242
|
'type': 'string',
|
1179
1243
|
},
|
1180
1244
|
},
|
1181
|
-
'
|
1182
|
-
'type': 'string',
|
1183
|
-
'case_insensitive_enum': [
|
1184
|
-
type.value
|
1185
|
-
for type in kubernetes_enums.KubernetesNetworkingMode
|
1186
|
-
]
|
1187
|
-
},
|
1188
|
-
'ports': {
|
1189
|
-
'type': 'string',
|
1190
|
-
'case_insensitive_enum': [
|
1191
|
-
type.value
|
1192
|
-
for type in kubernetes_enums.KubernetesPortMode
|
1193
|
-
]
|
1194
|
-
},
|
1195
|
-
'pod_config': {
|
1245
|
+
'context_configs': {
|
1196
1246
|
'type': 'object',
|
1197
1247
|
'required': [],
|
1198
|
-
|
1199
|
-
|
1200
|
-
|
1201
|
-
|
1202
|
-
|
1203
|
-
|
1204
|
-
|
1205
|
-
|
1206
|
-
# Disallow 'name' and 'namespace' keys in this dict
|
1207
|
-
'not': {
|
1208
|
-
'anyOf': [{
|
1209
|
-
'required': ['name']
|
1210
|
-
}, {
|
1211
|
-
'required': ['namespace']
|
1212
|
-
}]
|
1213
|
-
}
|
1214
|
-
},
|
1215
|
-
'provision_timeout': {
|
1216
|
-
'type': 'integer',
|
1217
|
-
},
|
1218
|
-
'autoscaler': {
|
1219
|
-
'type': 'string',
|
1220
|
-
'case_insensitive_enum': [
|
1221
|
-
type.value
|
1222
|
-
for type in kubernetes_enums.KubernetesAutoscalerType
|
1223
|
-
]
|
1224
|
-
},
|
1225
|
-
'high_availability': {
|
1226
|
-
'type': 'object',
|
1227
|
-
'required': [],
|
1228
|
-
'additionalProperties': False,
|
1229
|
-
'properties': {
|
1230
|
-
'storage_class_name': {
|
1231
|
-
'type': 'string',
|
1232
|
-
}
|
1233
|
-
}
|
1234
|
-
},
|
1235
|
-
'kueue': {
|
1236
|
-
'type': 'object',
|
1237
|
-
'required': [],
|
1238
|
-
'additionalProperties': False,
|
1239
|
-
'properties': {
|
1240
|
-
'local_queue_name': {
|
1241
|
-
'type': 'string',
|
1248
|
+
'properties': {},
|
1249
|
+
# Properties are kubernetes context names.
|
1250
|
+
'additionalProperties': {
|
1251
|
+
'type': 'object',
|
1252
|
+
'required': [],
|
1253
|
+
'additionalProperties': False,
|
1254
|
+
'properties': {
|
1255
|
+
**_CONTEXT_CONFIG_SCHEMA_KUBERNETES,
|
1242
1256
|
},
|
1243
1257
|
},
|
1244
1258
|
},
|
1259
|
+
**_CONTEXT_CONFIG_SCHEMA_KUBERNETES,
|
1245
1260
|
}
|
1246
1261
|
},
|
1247
1262
|
'ssh': {
|
@@ -1400,6 +1415,18 @@ def get_config_schema():
|
|
1400
1415
|
# Apply validation for URL
|
1401
1416
|
'pattern': r'^https?://.*$',
|
1402
1417
|
},
|
1418
|
+
'service_account_token': {
|
1419
|
+
'anyOf': [
|
1420
|
+
{
|
1421
|
+
'type': 'string',
|
1422
|
+
# Validate that token starts with sky_ prefix
|
1423
|
+
'pattern': r'^sky_.+$',
|
1424
|
+
},
|
1425
|
+
{
|
1426
|
+
'type': 'null',
|
1427
|
+
}
|
1428
|
+
]
|
1429
|
+
},
|
1403
1430
|
}
|
1404
1431
|
}
|
1405
1432
|
|
sky/utils/subprocess_utils.py
CHANGED
@@ -6,6 +6,7 @@ import random
|
|
6
6
|
import resource
|
7
7
|
import shlex
|
8
8
|
import subprocess
|
9
|
+
import sys
|
9
10
|
import threading
|
10
11
|
import time
|
11
12
|
import typing
|
@@ -16,7 +17,6 @@ import colorama
|
|
16
17
|
from sky import exceptions
|
17
18
|
from sky import sky_logging
|
18
19
|
from sky.adaptors import common as adaptors_common
|
19
|
-
from sky.skylet import constants
|
20
20
|
from sky.skylet import log_lib
|
21
21
|
from sky.utils import common_utils
|
22
22
|
from sky.utils import timeline
|
@@ -322,12 +322,8 @@ def kill_process_daemon(process_pid: int) -> None:
|
|
322
322
|
daemon_script = os.path.join(
|
323
323
|
os.path.dirname(os.path.abspath(log_lib.__file__)),
|
324
324
|
'subprocess_daemon.py')
|
325
|
-
python_path = subprocess.check_output(constants.SKY_GET_PYTHON_PATH_CMD,
|
326
|
-
shell=True,
|
327
|
-
stderr=subprocess.DEVNULL,
|
328
|
-
encoding='utf-8').strip()
|
329
325
|
daemon_cmd = [
|
330
|
-
|
326
|
+
sys.executable,
|
331
327
|
daemon_script,
|
332
328
|
'--parent-pid',
|
333
329
|
str(parent_pid),
|
sky/volumes/server/core.py
CHANGED
@@ -13,7 +13,9 @@ from sky import models
|
|
13
13
|
from sky import provision
|
14
14
|
from sky import sky_logging
|
15
15
|
from sky.utils import common_utils
|
16
|
+
from sky.utils import rich_utils
|
16
17
|
from sky.utils import status_lib
|
18
|
+
from sky.utils import ux_utils
|
17
19
|
|
18
20
|
logger = sky_logging.init_logger(__name__)
|
19
21
|
|
@@ -33,14 +35,14 @@ def volume_refresh():
|
|
33
35
|
'Skipping status refresh...')
|
34
36
|
continue
|
35
37
|
cloud = config.cloud
|
36
|
-
|
38
|
+
usedby_pods, _ = provision.get_volume_usedby(cloud, config)
|
37
39
|
with _volume_lock(volume_name):
|
38
40
|
latest_volume = global_user_state.get_volume_by_name(volume_name)
|
39
41
|
if latest_volume is None:
|
40
42
|
logger.warning(f'Volume {volume_name} not found.')
|
41
43
|
continue
|
42
44
|
status = latest_volume.get('status')
|
43
|
-
if not
|
45
|
+
if not usedby_pods:
|
44
46
|
if status != status_lib.VolumeStatus.READY:
|
45
47
|
logger.info(f'Update volume {volume_name} '
|
46
48
|
f'status to READY')
|
@@ -49,7 +51,7 @@ def volume_refresh():
|
|
49
51
|
else:
|
50
52
|
if status != status_lib.VolumeStatus.IN_USE:
|
51
53
|
logger.info(f'Update volume {volume_name} '
|
52
|
-
f'status to IN_USE, usedby: {
|
54
|
+
f'status to IN_USE, usedby: {usedby_pods}')
|
53
55
|
global_user_state.update_volume_status(
|
54
56
|
volume_name, status=status_lib.VolumeStatus.IN_USE)
|
55
57
|
|
@@ -74,42 +76,52 @@ def volume_list() -> List[Dict[str, Any]]:
|
|
74
76
|
'last_attached_at': int timestamp of last attachment,
|
75
77
|
'last_use': last command,
|
76
78
|
'status': sky.VolumeStatus,
|
79
|
+
'usedby_pods': List[str],
|
80
|
+
'usedby_clusters': List[str],
|
77
81
|
}
|
78
82
|
]
|
79
83
|
"""
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
84
|
+
with rich_utils.safe_status(ux_utils.spinner_message('Listing volumes')):
|
85
|
+
volumes = global_user_state.get_volumes()
|
86
|
+
all_users = global_user_state.get_all_users()
|
87
|
+
user_map = {user.id: user.name for user in all_users}
|
88
|
+
records = []
|
89
|
+
for volume in volumes:
|
90
|
+
volume_name = volume.get('name')
|
91
|
+
record = {
|
92
|
+
'name': volume_name,
|
93
|
+
'launched_at': volume.get('launched_at'),
|
94
|
+
'user_hash': volume.get('user_hash'),
|
95
|
+
'user_name': user_map.get(volume.get('user_hash'), ''),
|
96
|
+
'workspace': volume.get('workspace'),
|
97
|
+
'last_attached_at': volume.get('last_attached_at'),
|
98
|
+
'last_use': volume.get('last_use'),
|
99
|
+
'usedby_pods': [],
|
100
|
+
'usedby_clusters': [],
|
101
|
+
}
|
102
|
+
status = volume.get('status')
|
103
|
+
if status is not None:
|
104
|
+
record['status'] = status.value
|
105
|
+
else:
|
106
|
+
record['status'] = ''
|
107
|
+
config = volume.get('handle')
|
108
|
+
if config is None:
|
109
|
+
logger.warning(f'Volume {volume_name} has no handle.')
|
110
|
+
continue
|
111
|
+
cloud = config.cloud
|
112
|
+
usedby_pods, usedby_clusters = provision.get_volume_usedby(
|
113
|
+
cloud, config)
|
114
|
+
record['type'] = config.type
|
115
|
+
record['cloud'] = config.cloud
|
116
|
+
record['region'] = config.region
|
117
|
+
record['zone'] = config.zone
|
118
|
+
record['size'] = config.size
|
119
|
+
record['config'] = config.config
|
120
|
+
record['name_on_cloud'] = config.name_on_cloud
|
121
|
+
record['usedby_pods'] = usedby_pods
|
122
|
+
record['usedby_clusters'] = usedby_clusters
|
123
|
+
records.append(record)
|
124
|
+
return records
|
113
125
|
|
114
126
|
|
115
127
|
def volume_delete(names: List[str]) -> None:
|
@@ -122,20 +134,32 @@ def volume_delete(names: List[str]) -> None:
|
|
122
134
|
ValueError: If the volume does not exist
|
123
135
|
or is in use or has no handle.
|
124
136
|
"""
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
137
|
+
with rich_utils.safe_status(ux_utils.spinner_message('Deleting volumes')):
|
138
|
+
for name in names:
|
139
|
+
volume = global_user_state.get_volume_by_name(name)
|
140
|
+
if volume is None:
|
141
|
+
raise ValueError(f'Volume {name} not found.')
|
142
|
+
config = volume.get('handle')
|
143
|
+
if config is None:
|
144
|
+
raise ValueError(f'Volume {name} has no handle.')
|
145
|
+
cloud = config.cloud
|
146
|
+
usedby_pods, usedby_clusters = provision.get_volume_usedby(
|
147
|
+
cloud, config)
|
148
|
+
if usedby_clusters:
|
149
|
+
usedby_clusters_str = ', '.join(usedby_clusters)
|
150
|
+
cluster_str = 'clusters' if len(
|
151
|
+
usedby_clusters) > 1 else 'cluster'
|
152
|
+
raise ValueError(f'Volume {name} is used by {cluster_str}'
|
153
|
+
f' {usedby_clusters_str}.')
|
154
|
+
if usedby_pods:
|
155
|
+
usedby_pods_str = ', '.join(usedby_pods)
|
156
|
+
pod_str = 'pods' if len(usedby_pods) > 1 else 'pod'
|
157
|
+
raise ValueError(
|
158
|
+
f'Volume {name} is used by {pod_str} {usedby_pods_str}.')
|
159
|
+
logger.debug(f'Deleting volume {name} with config {config}')
|
160
|
+
with _volume_lock(name):
|
161
|
+
provision.delete_volume(cloud, config)
|
162
|
+
global_user_state.delete_volume(name)
|
139
163
|
|
140
164
|
|
141
165
|
def volume_apply(name: str, volume_type: str, cloud: str, region: Optional[str],
|
@@ -153,34 +177,35 @@ def volume_apply(name: str, volume_type: str, cloud: str, region: Optional[str],
|
|
153
177
|
config: The configuration of the volume.
|
154
178
|
|
155
179
|
"""
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
180
|
+
with rich_utils.safe_status(ux_utils.spinner_message('Creating volume')):
|
181
|
+
# Reuse the method for cluster name on cloud to
|
182
|
+
# generate the storage name on cloud.
|
183
|
+
cloud_obj = sky.CLOUD_REGISTRY.from_str(cloud)
|
184
|
+
assert cloud_obj is not None
|
185
|
+
name_uuid = str(uuid.uuid4())[:6]
|
186
|
+
name_on_cloud = common_utils.make_cluster_name_on_cloud(
|
187
|
+
name, max_length=cloud_obj.max_cluster_name_length())
|
188
|
+
name_on_cloud += '-' + name_uuid
|
189
|
+
config = models.VolumeConfig(
|
190
|
+
name=name,
|
191
|
+
type=volume_type,
|
192
|
+
cloud=str(cloud_obj),
|
193
|
+
region=region,
|
194
|
+
zone=zone,
|
195
|
+
size=size,
|
196
|
+
config=config,
|
197
|
+
name_on_cloud=name_on_cloud,
|
198
|
+
)
|
199
|
+
logger.debug(
|
200
|
+
f'Creating volume {name} on cloud {cloud} with config {config}')
|
201
|
+
with _volume_lock(name):
|
202
|
+
current_volume = global_user_state.get_volume_by_name(name)
|
203
|
+
if current_volume is not None:
|
204
|
+
logger.info(f'Volume {name} already exists.')
|
205
|
+
return
|
206
|
+
config = provision.apply_volume(cloud, config)
|
207
|
+
global_user_state.add_volume(name, config,
|
208
|
+
status_lib.VolumeStatus.READY)
|
184
209
|
|
185
210
|
|
186
211
|
@contextlib.contextmanager
|
sky/volumes/utils.py
CHANGED
@@ -6,6 +6,8 @@ from typing import Any, Dict, List, Optional
|
|
6
6
|
import prettytable
|
7
7
|
|
8
8
|
from sky import sky_logging
|
9
|
+
from sky.skylet import constants
|
10
|
+
from sky.utils import common_utils
|
9
11
|
from sky.utils import log_utils
|
10
12
|
from sky.volumes import volume
|
11
13
|
|
@@ -44,12 +46,12 @@ class PVCVolumeTable(VolumeTable):
|
|
44
46
|
|
45
47
|
def _create_table(self, show_all: bool = False) -> prettytable.PrettyTable:
|
46
48
|
"""Create the PVC volume table."""
|
47
|
-
# If show_all is True, show the table with the columns:
|
48
|
-
# NAME, TYPE, INFRA, SIZE, USER, WORKSPACE,
|
49
|
-
# AGE, LAST_USE, STATUS
|
50
49
|
# If show_all is False, show the table with the columns:
|
51
50
|
# NAME, TYPE, INFRA, SIZE, USER, WORKSPACE,
|
52
|
-
# AGE,
|
51
|
+
# AGE, STATUS, LAST_USE, USED_BY
|
52
|
+
# If show_all is True, show the table with the columns:
|
53
|
+
# NAME, TYPE, INFRA, SIZE, USER, WORKSPACE,
|
54
|
+
# AGE, STATUS, LAST_USE, USED_BY, NAME_ON_CLOUD
|
53
55
|
# STORAGE_CLASS, ACCESS_MODE
|
54
56
|
|
55
57
|
if show_all:
|
@@ -63,6 +65,7 @@ class PVCVolumeTable(VolumeTable):
|
|
63
65
|
'AGE',
|
64
66
|
'STATUS',
|
65
67
|
'LAST_USE',
|
68
|
+
'USED_BY',
|
66
69
|
'NAME_ON_CLOUD',
|
67
70
|
'STORAGE_CLASS',
|
68
71
|
'ACCESS_MODE',
|
@@ -78,6 +81,7 @@ class PVCVolumeTable(VolumeTable):
|
|
78
81
|
'AGE',
|
79
82
|
'STATUS',
|
80
83
|
'LAST_USE',
|
84
|
+
'USED_BY',
|
81
85
|
]
|
82
86
|
|
83
87
|
table = log_utils.create_table(columns)
|
@@ -98,6 +102,18 @@ class PVCVolumeTable(VolumeTable):
|
|
98
102
|
size = row.get('size', '')
|
99
103
|
if size:
|
100
104
|
size = f'{size}Gi'
|
105
|
+
usedby_str = '-'
|
106
|
+
usedby_clusters = row.get('usedby_clusters')
|
107
|
+
usedby_pods = row.get('usedby_pods')
|
108
|
+
if usedby_clusters:
|
109
|
+
usedby_str = f'{", ".join(usedby_clusters)}'
|
110
|
+
elif usedby_pods:
|
111
|
+
usedby_str = f'{", ".join(usedby_pods)}'
|
112
|
+
if show_all:
|
113
|
+
usedby = usedby_str
|
114
|
+
else:
|
115
|
+
usedby = common_utils.truncate_long_string(
|
116
|
+
usedby_str, constants.USED_BY_TRUNC_LENGTH)
|
101
117
|
infra = _get_infra_str(row.get('cloud'), row.get('region'),
|
102
118
|
row.get('zone'))
|
103
119
|
table_row = [
|
@@ -107,9 +123,10 @@ class PVCVolumeTable(VolumeTable):
|
|
107
123
|
size,
|
108
124
|
row.get('user_name', '-'),
|
109
125
|
row.get('workspace', '-'),
|
110
|
-
log_utils.
|
126
|
+
log_utils.human_duration(row.get('launched_at', 0)),
|
111
127
|
row.get('status', ''),
|
112
128
|
last_attached_at_str,
|
129
|
+
usedby,
|
113
130
|
]
|
114
131
|
if show_all:
|
115
132
|
table_row.append(row.get('name_on_cloud', ''))
|