sagemaker-hyperpod 3.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sagemaker/__init__.py +0 -0
- sagemaker/hyperpod/__init__.py +2 -0
- sagemaker/hyperpod/cli/__init__.py +0 -0
- sagemaker/hyperpod/cli/clients/__init__.py +12 -0
- sagemaker/hyperpod/cli/clients/kubernetes_client.py +365 -0
- sagemaker/hyperpod/cli/commands/__init__.py +0 -0
- sagemaker/hyperpod/cli/commands/cluster.py +627 -0
- sagemaker/hyperpod/cli/commands/inference.py +659 -0
- sagemaker/hyperpod/cli/commands/training.py +347 -0
- sagemaker/hyperpod/cli/constants/__init__.py +0 -0
- sagemaker/hyperpod/cli/constants/command_constants.py +105 -0
- sagemaker/hyperpod/cli/constants/exception_constants.py +13 -0
- sagemaker/hyperpod/cli/constants/hyperpod_instance_types.py +100 -0
- sagemaker/hyperpod/cli/constants/kueue_constants.py +17 -0
- sagemaker/hyperpod/cli/constants/pytorch_constants.py +15 -0
- sagemaker/hyperpod/cli/hyp_cli.py +132 -0
- sagemaker/hyperpod/cli/inference_utils.py +121 -0
- sagemaker/hyperpod/cli/service/__init__.py +12 -0
- sagemaker/hyperpod/cli/service/cancel_training_job.py +70 -0
- sagemaker/hyperpod/cli/service/discover_namespaces.py +108 -0
- sagemaker/hyperpod/cli/service/exec_command.py +75 -0
- sagemaker/hyperpod/cli/service/get_logs.py +136 -0
- sagemaker/hyperpod/cli/service/get_namespaces.py +59 -0
- sagemaker/hyperpod/cli/service/get_training_job.py +110 -0
- sagemaker/hyperpod/cli/service/list_pods.py +138 -0
- sagemaker/hyperpod/cli/service/list_training_jobs.py +178 -0
- sagemaker/hyperpod/cli/service/self_subject_access_review.py +47 -0
- sagemaker/hyperpod/cli/telemetry/__init__.py +12 -0
- sagemaker/hyperpod/cli/telemetry/user_agent.py +36 -0
- sagemaker/hyperpod/cli/templates/__init__.py +12 -0
- sagemaker/hyperpod/cli/templates/k8s_pytorch_job_template.py +74 -0
- sagemaker/hyperpod/cli/training_utils.py +200 -0
- sagemaker/hyperpod/cli/utils.py +183 -0
- sagemaker/hyperpod/cli/validators/__init__.py +12 -0
- sagemaker/hyperpod/cli/validators/cluster_validator.py +52 -0
- sagemaker/hyperpod/cli/validators/job_validator.py +346 -0
- sagemaker/hyperpod/cli/validators/validator.py +75 -0
- sagemaker/hyperpod/common/__init__.py +0 -0
- sagemaker/hyperpod/common/config/__init__.py +1 -0
- sagemaker/hyperpod/common/config/metadata.py +18 -0
- sagemaker/hyperpod/common/utils.py +299 -0
- sagemaker/hyperpod/inference/__init__.py +11 -0
- sagemaker/hyperpod/inference/config/__init__.py +0 -0
- sagemaker/hyperpod/inference/config/constants.py +18 -0
- sagemaker/hyperpod/inference/config/hp_endpoint_config.py +767 -0
- sagemaker/hyperpod/inference/config/hp_jumpstart_endpoint_config.py +572 -0
- sagemaker/hyperpod/inference/hp_endpoint.py +203 -0
- sagemaker/hyperpod/inference/hp_endpoint_base.py +223 -0
- sagemaker/hyperpod/inference/hp_jumpstart_endpoint.py +232 -0
- sagemaker/hyperpod/observability/MonitoringConfig.py +10 -0
- sagemaker/hyperpod/observability/__init__.py +0 -0
- sagemaker/hyperpod/observability/constants.py +2 -0
- sagemaker/hyperpod/observability/utils.py +64 -0
- sagemaker/hyperpod/training/__init__.py +9 -0
- sagemaker/hyperpod/training/config/__init__.py +1 -0
- sagemaker/hyperpod/training/config/hyperpod_pytorch_job_config.py +2977 -0
- sagemaker/hyperpod/training/config/hyperpod_pytorch_job_status.py +3083 -0
- sagemaker/hyperpod/training/hyperpod_pytorch_job.py +253 -0
- sagemaker_hyperpod-3.0.0.dist-info/METADATA +534 -0
- sagemaker_hyperpod-3.0.0.dist-info/RECORD +66 -0
- sagemaker_hyperpod-3.0.0.dist-info/WHEEL +5 -0
- sagemaker_hyperpod-3.0.0.dist-info/entry_points.txt +2 -0
- sagemaker_hyperpod-3.0.0.dist-info/licenses/LICENSE +175 -0
- sagemaker_hyperpod-3.0.0.dist-info/licenses/NOTICE +1 -0
- sagemaker_hyperpod-3.0.0.dist-info/top_level.txt +1 -0
- sagemaker_hyperpod-3.0.0.dist-info/zip-safe +1 -0
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import pkgutil
|
|
3
|
+
import click
|
|
4
|
+
from typing import Callable, Optional, Mapping, Type
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def load_schema_for_version(
|
|
8
|
+
version: str,
|
|
9
|
+
base_package: str,
|
|
10
|
+
) -> dict:
|
|
11
|
+
"""
|
|
12
|
+
Load schema.json from the top-level <base_package>.vX_Y_Z package.
|
|
13
|
+
"""
|
|
14
|
+
ver_pkg = f"{base_package}.v{version.replace('.', '_')}"
|
|
15
|
+
raw = pkgutil.get_data(ver_pkg, "schema.json")
|
|
16
|
+
if raw is None:
|
|
17
|
+
raise click.ClickException(
|
|
18
|
+
f"Could not load schema.json for version {version} "
|
|
19
|
+
f"(looked in package {ver_pkg})"
|
|
20
|
+
)
|
|
21
|
+
return json.loads(raw)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def generate_click_command(
|
|
25
|
+
*,
|
|
26
|
+
version_key: Optional[str] = None,
|
|
27
|
+
schema_pkg: str = "hyperpod_jumpstart_inference_template",
|
|
28
|
+
registry: Mapping[str, Type] = None,
|
|
29
|
+
) -> Callable:
|
|
30
|
+
"""
|
|
31
|
+
Decorator factory that:
|
|
32
|
+
1) Injects click.options from the JSON Schema under `schema_pkg`
|
|
33
|
+
2) At runtime, pops `version`, builds the flat model from `registry`, calls .to_domain()
|
|
34
|
+
3) Finally invokes your handler as `func(version, domain_config)`
|
|
35
|
+
- `version_key`: if given, hard-codes the version (no --version flag injected)
|
|
36
|
+
- `schema_pkg`: the importable package root to read schema.json from
|
|
37
|
+
- `registry`: a dict mapping version → flat‐model class, e.g. hyperpod_pytorch_job_template.registry.SCHEMA_REGISTRY
|
|
38
|
+
"""
|
|
39
|
+
if registry is None:
|
|
40
|
+
raise ValueError("You must pass a registry mapping version→Model")
|
|
41
|
+
|
|
42
|
+
def decorator(func: Callable) -> Callable:
|
|
43
|
+
# Parser for the single JSON‐dict env var flag
|
|
44
|
+
def _parse_json_flag(ctx, param, value):
|
|
45
|
+
if value is None:
|
|
46
|
+
return None
|
|
47
|
+
try:
|
|
48
|
+
return json.loads(value)
|
|
49
|
+
except json.JSONDecodeError as e:
|
|
50
|
+
raise click.BadParameter(f"{param.name!r} must be valid JSON: {e}")
|
|
51
|
+
|
|
52
|
+
# Parser for list flags
|
|
53
|
+
def _parse_list_flag(ctx, param, value):
|
|
54
|
+
if value is None:
|
|
55
|
+
return None
|
|
56
|
+
# Remove brackets and split by comma
|
|
57
|
+
value = value.strip("[]")
|
|
58
|
+
return [item.strip() for item in value.split(",") if item.strip()]
|
|
59
|
+
|
|
60
|
+
# 1) the wrapper click will call
|
|
61
|
+
def wrapped_func(*args, **kwargs):
|
|
62
|
+
# extract version
|
|
63
|
+
version = version_key or kwargs.pop("version", "1.0")
|
|
64
|
+
debug = kwargs.pop("debug", False)
|
|
65
|
+
|
|
66
|
+
# look up the model class
|
|
67
|
+
Model = registry.get(version)
|
|
68
|
+
if Model is None:
|
|
69
|
+
raise click.ClickException(f"Unsupported schema version: {version}")
|
|
70
|
+
|
|
71
|
+
# validate & to_domain
|
|
72
|
+
flat = Model(**kwargs)
|
|
73
|
+
domain_config = flat.to_domain()
|
|
74
|
+
|
|
75
|
+
# call your handler
|
|
76
|
+
return func(version, debug, domain_config)
|
|
77
|
+
|
|
78
|
+
# 2) inject click options from JSON Schema
|
|
79
|
+
excluded_props = set(["version"])
|
|
80
|
+
if schema_pkg == "hyperpod_jumpstart_inference_template":
|
|
81
|
+
wrapped_func = click.option(
|
|
82
|
+
"--env",
|
|
83
|
+
callback=_parse_json_flag,
|
|
84
|
+
type=str,
|
|
85
|
+
default=None,
|
|
86
|
+
help=(
|
|
87
|
+
"JSON object of environment variables, e.g. "
|
|
88
|
+
'\'{"VAR1":"foo","VAR2":"bar"}\''
|
|
89
|
+
),
|
|
90
|
+
metavar="JSON",
|
|
91
|
+
)(wrapped_func)
|
|
92
|
+
wrapped_func = click.option(
|
|
93
|
+
"--resources-limits",
|
|
94
|
+
callback=_parse_json_flag,
|
|
95
|
+
help='JSON object of resource limits, e.g. \'{"cpu":"2","memory":"4Gi"}\'',
|
|
96
|
+
metavar="JSON",
|
|
97
|
+
)(wrapped_func)
|
|
98
|
+
|
|
99
|
+
wrapped_func = click.option(
|
|
100
|
+
"--resources-requests",
|
|
101
|
+
callback=_parse_json_flag,
|
|
102
|
+
help='JSON object of resource requests, e.g. \'{"cpu":"1","memory":"2Gi"}\'',
|
|
103
|
+
metavar="JSON",
|
|
104
|
+
)(wrapped_func)
|
|
105
|
+
|
|
106
|
+
excluded_props = set(
|
|
107
|
+
["version", "env", "resources_limits", "resources_requests"]
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
elif schema_pkg == "hyperpod_pytorch_job_template":
|
|
111
|
+
wrapped_func = click.option(
|
|
112
|
+
"--environment",
|
|
113
|
+
callback=_parse_json_flag,
|
|
114
|
+
type=str,
|
|
115
|
+
default=None,
|
|
116
|
+
help=(
|
|
117
|
+
"JSON object of environment variables, e.g. "
|
|
118
|
+
'\'{"VAR1":"foo","VAR2":"bar"}\''
|
|
119
|
+
),
|
|
120
|
+
metavar="JSON",
|
|
121
|
+
)(wrapped_func)
|
|
122
|
+
wrapped_func = click.option(
|
|
123
|
+
"--label_selector",
|
|
124
|
+
callback=_parse_json_flag,
|
|
125
|
+
help='JSON object of resource limits, e.g. \'{"cpu":"2","memory":"4Gi"}\'',
|
|
126
|
+
metavar="JSON",
|
|
127
|
+
)(wrapped_func)
|
|
128
|
+
|
|
129
|
+
# Add list options
|
|
130
|
+
list_params = {
|
|
131
|
+
"command": "List of command arguments",
|
|
132
|
+
"args": "List of script arguments, e.g. '[--batch-size, 32, --learning-rate, 0.001]'",
|
|
133
|
+
"volumes": "List of volumes, e.g. '[vol1, vol2, vol3]'",
|
|
134
|
+
"persistent_volume_claims": "List of persistent volume claims, e.g. '[pvc1, pvc2]'",
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
for param_name, help_text in list_params.items():
|
|
138
|
+
wrapped_func = click.option(
|
|
139
|
+
f"--{param_name}",
|
|
140
|
+
callback=_parse_list_flag,
|
|
141
|
+
type=str,
|
|
142
|
+
default=None,
|
|
143
|
+
help=help_text,
|
|
144
|
+
metavar="LIST",
|
|
145
|
+
)(wrapped_func)
|
|
146
|
+
|
|
147
|
+
excluded_props = set(
|
|
148
|
+
[
|
|
149
|
+
"version",
|
|
150
|
+
"environment",
|
|
151
|
+
"label_selector",
|
|
152
|
+
"command",
|
|
153
|
+
"args",
|
|
154
|
+
"volumes",
|
|
155
|
+
"persistent_volume_claims",
|
|
156
|
+
]
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
schema = load_schema_for_version(version_key or "1.0", schema_pkg)
|
|
160
|
+
props = schema.get("properties", {})
|
|
161
|
+
reqs = set(schema.get("required", []))
|
|
162
|
+
|
|
163
|
+
# reverse so flags appear in the same order as in schema.json
|
|
164
|
+
for name, spec in reversed(list(props.items())):
|
|
165
|
+
if name in excluded_props:
|
|
166
|
+
continue
|
|
167
|
+
|
|
168
|
+
# type inference
|
|
169
|
+
if "enum" in spec:
|
|
170
|
+
ctype = click.Choice(spec["enum"])
|
|
171
|
+
elif spec.get("type") == "integer":
|
|
172
|
+
ctype = int
|
|
173
|
+
elif spec.get("type") == "number":
|
|
174
|
+
ctype = float
|
|
175
|
+
elif spec.get("type") == "boolean":
|
|
176
|
+
ctype = bool
|
|
177
|
+
else:
|
|
178
|
+
ctype = str
|
|
179
|
+
|
|
180
|
+
wrapped_func = click.option(
|
|
181
|
+
f"--{name.replace('_','-')}",
|
|
182
|
+
required=(name in reqs),
|
|
183
|
+
default=spec.get("default", None),
|
|
184
|
+
show_default=("default" in spec),
|
|
185
|
+
type=ctype,
|
|
186
|
+
help=spec.get("description", ""),
|
|
187
|
+
)(wrapped_func)
|
|
188
|
+
|
|
189
|
+
# 3) if no hard-coded version_key, inject the top-level --version flag
|
|
190
|
+
if version_key is None:
|
|
191
|
+
wrapped_func = click.option(
|
|
192
|
+
"--version",
|
|
193
|
+
default="1.0",
|
|
194
|
+
show_default=True,
|
|
195
|
+
help="Schema version to use",
|
|
196
|
+
)(wrapped_func)
|
|
197
|
+
|
|
198
|
+
return wrapped_func
|
|
199
|
+
|
|
200
|
+
return decorator
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License"). You
|
|
4
|
+
# may not use this file except in compliance with the License. A copy of
|
|
5
|
+
# the License is located at
|
|
6
|
+
#
|
|
7
|
+
# http://aws.amazon.com/apache2.0/
|
|
8
|
+
#
|
|
9
|
+
# or in the "license" file accompanying this file. This file is
|
|
10
|
+
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
|
|
11
|
+
# ANY KIND, either express or implied. See the License for the specific
|
|
12
|
+
# language governing permissions and limitations under the License.
|
|
13
|
+
import logging
|
|
14
|
+
import re
|
|
15
|
+
import json
|
|
16
|
+
|
|
17
|
+
import boto3
|
|
18
|
+
import botocore
|
|
19
|
+
from botocore.config import Config
|
|
20
|
+
|
|
21
|
+
from sagemaker.hyperpod.cli.constants.command_constants import (
|
|
22
|
+
GENERATED_LAUNCHER_CONFIG_FILE_PATH,
|
|
23
|
+
HYPERPOD_CLUSTER_CONTEXT_FILE_NAME,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def get_name_from_arn(arn: str) -> str:
|
|
28
|
+
"""
|
|
29
|
+
Parse the EKS cluster name from an EKS ARN.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
arn (str): The ARN of the EKS cluster.
|
|
33
|
+
|
|
34
|
+
Returns: str: The name of the EKS cluster if parsing is
|
|
35
|
+
successful, otherwise raise RuntimeError.
|
|
36
|
+
"""
|
|
37
|
+
# Define the regex pattern to match the EKS ARN and capture the cluster name
|
|
38
|
+
pattern = r"arn:aws:eks:[\w-]+:\d+:cluster/([\w-]+)"
|
|
39
|
+
match = re.match(pattern, arn)
|
|
40
|
+
|
|
41
|
+
if match:
|
|
42
|
+
return match.group(1)
|
|
43
|
+
else:
|
|
44
|
+
raise RuntimeError("cannot get EKS cluster name")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def setup_logger(
|
|
48
|
+
name: str,
|
|
49
|
+
logging_level: int = logging.ERROR,
|
|
50
|
+
) -> logging.Logger:
|
|
51
|
+
"""
|
|
52
|
+
Set up a logger with a console handler and a formatter.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
name (str): The name of the logger.
|
|
56
|
+
logging_level (int): The logging level.
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
logging.Logger: The configured logger instance.
|
|
60
|
+
"""
|
|
61
|
+
# Create a logger
|
|
62
|
+
logger = logging.getLogger(name)
|
|
63
|
+
logger.setLevel(logging_level) # Set the log level to ERROR
|
|
64
|
+
|
|
65
|
+
# Create a console handler
|
|
66
|
+
console_handler = logging.StreamHandler()
|
|
67
|
+
console_handler.setLevel(logging_level) # Set the log level for the handler
|
|
68
|
+
|
|
69
|
+
# Create a formatter and set it for the handler
|
|
70
|
+
formatter = logging.Formatter(
|
|
71
|
+
"%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
|
72
|
+
datefmt="%Y-%m-%d %H:%M:%S",
|
|
73
|
+
)
|
|
74
|
+
console_handler.setFormatter(formatter)
|
|
75
|
+
|
|
76
|
+
# Add the handler to the logger
|
|
77
|
+
logger.addHandler(console_handler)
|
|
78
|
+
|
|
79
|
+
return logger
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def set_logging_level(
|
|
83
|
+
logger: logging.Logger,
|
|
84
|
+
logging_level: int,
|
|
85
|
+
):
|
|
86
|
+
logger.setLevel(logging_level)
|
|
87
|
+
logger.handlers[0].setLevel(logging_level)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def get_sagemaker_client(
|
|
91
|
+
session: boto3.Session, config: Config = None
|
|
92
|
+
) -> botocore.client.BaseClient:
|
|
93
|
+
return session.client(
|
|
94
|
+
service_name="sagemaker",
|
|
95
|
+
config=config,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def store_current_hyperpod_context(data):
|
|
100
|
+
with open(
|
|
101
|
+
GENERATED_LAUNCHER_CONFIG_FILE_PATH + HYPERPOD_CLUSTER_CONTEXT_FILE_NAME,
|
|
102
|
+
"w",
|
|
103
|
+
) as hyperpod_current_context:
|
|
104
|
+
hyperpod_current_context.write(json.dumps(data, indent=4, default=str))
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _retrieve_current_hyperpod_context():
|
|
108
|
+
with open(
|
|
109
|
+
GENERATED_LAUNCHER_CONFIG_FILE_PATH + HYPERPOD_CLUSTER_CONTEXT_FILE_NAME,
|
|
110
|
+
"r",
|
|
111
|
+
) as file:
|
|
112
|
+
return json.load(file)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _validate_link(console_url):
|
|
116
|
+
pattern = "https:\/\/([a-z0-9-]+).console.aws.amazon.com\/sagemaker\/home\?region=([a-z0-9-]+)#\/cluster-management\/([a-zA-Z0-9-]+)"
|
|
117
|
+
match = re.match(pattern, console_url)
|
|
118
|
+
if match:
|
|
119
|
+
return True
|
|
120
|
+
else:
|
|
121
|
+
return False
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def validate_region_and_cluster_name(region, cluster_name):
|
|
125
|
+
output = False
|
|
126
|
+
region_char_list = region.split("-")
|
|
127
|
+
|
|
128
|
+
if len(region_char_list) != 3:
|
|
129
|
+
return False
|
|
130
|
+
|
|
131
|
+
region_prefix_match = re.match("[a-z]+", region_char_list[0])
|
|
132
|
+
region_match = re.match("[a-z]+", region_char_list[1])
|
|
133
|
+
region_suffix_match = re.match("[0-9]+", region_char_list[2])
|
|
134
|
+
|
|
135
|
+
region_prefix_length = len(region_char_list[0])
|
|
136
|
+
region_length = len(region_char_list[1])
|
|
137
|
+
region_suffix_length = len(region_char_list[2])
|
|
138
|
+
|
|
139
|
+
cluster_name_match = re.match("[a-zA-Z0-9-]+", cluster_name)
|
|
140
|
+
|
|
141
|
+
if (
|
|
142
|
+
region_prefix_match
|
|
143
|
+
and region_match
|
|
144
|
+
and region_suffix_match
|
|
145
|
+
and region_prefix_length == 2
|
|
146
|
+
and region_suffix_length == 1
|
|
147
|
+
and region_length >= 4
|
|
148
|
+
and region_length < 10
|
|
149
|
+
and cluster_name_match
|
|
150
|
+
and len(cluster_name) >= 1
|
|
151
|
+
and len(cluster_name) <= 63
|
|
152
|
+
):
|
|
153
|
+
output = True
|
|
154
|
+
return output
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def get_cluster_console_url():
|
|
158
|
+
hyperpod_context_cluster = _retrieve_current_hyperpod_context()
|
|
159
|
+
console_url = None
|
|
160
|
+
if (
|
|
161
|
+
hyperpod_context_cluster
|
|
162
|
+
and hyperpod_context_cluster.get("ClusterArn")
|
|
163
|
+
and hyperpod_context_cluster.get("ClusterName")
|
|
164
|
+
):
|
|
165
|
+
region = hyperpod_context_cluster.get("ClusterArn").split(":")[3]
|
|
166
|
+
cluster_name = hyperpod_context_cluster.get("ClusterName")
|
|
167
|
+
|
|
168
|
+
console_url = (
|
|
169
|
+
f"https://{region}.console.aws.amazon.com/sagemaker/"
|
|
170
|
+
f"home?region={region}#/cluster-management/{cluster_name}"
|
|
171
|
+
)
|
|
172
|
+
if _validate_link(console_url) and validate_region_and_cluster_name(region, cluster_name):
|
|
173
|
+
return console_url
|
|
174
|
+
return None
|
|
175
|
+
|
|
176
|
+
def get_eks_cluster_name():
|
|
177
|
+
hyperpod_context_cluster = _retrieve_current_hyperpod_context()
|
|
178
|
+
eks_cluster_arn = hyperpod_context_cluster.get("Orchestrator", {}).get("Eks", {}).get("ClusterArn", '')
|
|
179
|
+
return eks_cluster_arn.split('cluster/')[-1]
|
|
180
|
+
|
|
181
|
+
def get_hyperpod_cluster_region():
|
|
182
|
+
hyperpod_context_cluster = _retrieve_current_hyperpod_context()
|
|
183
|
+
return hyperpod_context_cluster.get("ClusterArn").split(":")[3]
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License"). You
|
|
4
|
+
# may not use this file except in compliance with the License. A copy of
|
|
5
|
+
# the License is located at
|
|
6
|
+
#
|
|
7
|
+
# http://aws.amazon.com/apache2.0/
|
|
8
|
+
#
|
|
9
|
+
# or in the "license" file accompanying this file. This file is
|
|
10
|
+
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
|
|
11
|
+
# ANY KIND, either express or implied. See the License for the specific
|
|
12
|
+
# language governing permissions and limitations under the License.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License"). You
|
|
4
|
+
# may not use this file except in compliance with the License. A copy of
|
|
5
|
+
# the License is located at
|
|
6
|
+
#
|
|
7
|
+
# http://aws.amazon.com/apache2.0/
|
|
8
|
+
#
|
|
9
|
+
# or in the "license" file accompanying this file. This file is
|
|
10
|
+
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
|
|
11
|
+
# ANY KIND, either express or implied. See the License for the specific
|
|
12
|
+
# language governing permissions and limitations under the License.
|
|
13
|
+
from typing import Optional
|
|
14
|
+
|
|
15
|
+
import boto3
|
|
16
|
+
from botocore.exceptions import ClientError
|
|
17
|
+
|
|
18
|
+
from sagemaker.hyperpod.cli.utils import setup_logger
|
|
19
|
+
from sagemaker.hyperpod.cli.validators.validator import (
|
|
20
|
+
Validator,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
logger = setup_logger(__name__)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class ClusterValidator(Validator):
|
|
27
|
+
def __init__(self):
|
|
28
|
+
super().__init__()
|
|
29
|
+
|
|
30
|
+
def validate_cluster_and_get_eks_arn(
|
|
31
|
+
self, cluster_name: str, sm_client: boto3.client
|
|
32
|
+
) -> Optional[str]:
|
|
33
|
+
try:
|
|
34
|
+
hp_cluster_details = sm_client.describe_cluster(ClusterName=cluster_name)
|
|
35
|
+
if (
|
|
36
|
+
"Orchestrator" not in hp_cluster_details
|
|
37
|
+
or "Eks" not in hp_cluster_details["Orchestrator"]
|
|
38
|
+
):
|
|
39
|
+
logger.warning(
|
|
40
|
+
f"HyperPod cluster {cluster_name} Orchestrator not exist or is not Eks."
|
|
41
|
+
)
|
|
42
|
+
return None
|
|
43
|
+
return hp_cluster_details["Orchestrator"]["Eks"]["ClusterArn"]
|
|
44
|
+
except ClientError as e:
|
|
45
|
+
if e.response["Error"]["Code"] == "ResourceNotFoundException":
|
|
46
|
+
logger.error(f"HyperPod cluster {cluster_name} not found.")
|
|
47
|
+
else:
|
|
48
|
+
logger.error(f"Validate HyperPod cluster {cluster_name} error: {e}")
|
|
49
|
+
return None
|
|
50
|
+
except Exception as e:
|
|
51
|
+
logger.error(f"Validate HyperPod cluster {cluster_name} error: {e}")
|
|
52
|
+
return None
|