Skip to content

Commit 693c623

Browse files
committed
feat: Add DAG for validating tpu-info CLI tool functionality in TPU worker pods
1 parent 8035568 commit 693c623

1 file changed

Lines changed: 39 additions & 96 deletions

File tree

dags/tpu_observability/tpu_info_help_validation_dags.py renamed to dags/tpu_observability/tpu_info_cli_validation_dags.py

Lines changed: 39 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@
1818
"""
1919

2020
import datetime
21-
import tempfile
2221
import os
22+
import subprocess
23+
import tempfile
24+
from typing import List
2325

2426
from airflow import models
2527
from airflow.decorators import task
@@ -34,29 +36,32 @@
3436
from dags.tpu_observability.configs.common import MachineConfigMap, GCS_CONFIG_PATH
3537

3638

37-
def _get_tpu_info_help_output(info: node_pool.Info, pod_name: str) -> str:
38-
"""
39-
Retrieves the raw output of `tpu-info -help` from a specific pod.
40-
"""
39+
def run_command(info, pod_name: str, tpu_args: str) -> str:
40+
"""Helper to handle KUBECONFIG and execute kubectl."""
4141
with tempfile.NamedTemporaryFile() as temp_config_file:
4242
env = os.environ.copy()
4343
env["KUBECONFIG"] = temp_config_file.name
4444

4545
cmd = " && ".join([
4646
jobset.Command.get_credentials_command(info),
47-
f"kubectl exec {pod_name} -n default -- tpu-info -help",
47+
f"kubectl exec {pod_name} -n default -- {tpu_args}",
4848
])
49-
5049
return subprocess.run_exec(cmd, env=env)
5150

5251

52+
def check_output_contains(output: str, patterns: List[str], context: str):
53+
"""Helper to verify expected strings in output."""
54+
for pattern in patterns:
55+
if pattern not in output:
56+
raise AssertionError(
57+
f"Validation failed for '{context}': Missing '{pattern}'."
58+
)
59+
60+
5361
@task
54-
def validate_tpu_info_help(info: node_pool.Info, pod_name: str) -> str:
55-
"""
56-
Validates that the `tpu-info -help` output contains all required fields.
57-
"""
58-
output = _get_tpu_info_help_output(info, pod_name)
59-
required_patterns = [
62+
def validate_help(info, pod_name: str) -> str:
63+
output = run_command(info, pod_name, "tpu-info -help")
64+
patterns = [
6065
"Display TPU info and metrics.",
6166
"options:",
6267
"-h, --help",
@@ -66,99 +71,37 @@ def validate_tpu_info_help(info: node_pool.Info, pod_name: str) -> str:
6671
"--rate RATE",
6772
"--list_metrics",
6873
]
69-
70-
for pattern in required_patterns:
71-
if pattern not in output:
72-
raise AssertionError(
73-
"Validation failed: Missing expected string "
74-
f"'{pattern}' in tpu-info help.\n"
75-
f"Output received:\n{output}"
76-
)
77-
74+
check_output_contains(output, patterns, "tpu-info -help")
7875
return output
7976

8077

81-
def _get_tpu_version_output(info: node_pool.Info, pod_name: str) -> str:
82-
"""
83-
Executes the version command in the pod to get libtpu version and accelerator type.
84-
"""
85-
with tempfile.NamedTemporaryFile() as temp_config_file:
86-
env = os.environ.copy()
87-
env["KUBECONFIG"] = temp_config_file.name
88-
89-
cmd = " && ".join([
90-
jobset.Command.get_credentials_command(info),
91-
f"kubectl exec {pod_name} -n default -- tpu-info --version",
92-
])
93-
return subprocess.run_exec(cmd, env=env)
94-
95-
9678
@task
97-
def validate_tpu_info_version(info: node_pool.Info, pod_name: str) -> str:
98-
"""
99-
Validates that `tpu-info --version` returns version and accelerator info.
100-
"""
101-
output = _get_tpu_version_output(info, pod_name)
102-
required_patterns = [
103-
"tpu-info version:",
104-
"libtpu version:",
105-
"accelerator type:",
106-
]
107-
108-
for pattern in required_patterns:
109-
if pattern not in output:
110-
raise AssertionError(
111-
f"Validation failed: Missing expected string '{pattern}' "
112-
f"in tpu-info --version output.\nOutput:\n{output}"
113-
)
114-
79+
def validate_version(info, pod_name: str) -> str:
80+
output = run_command(info, pod_name, "tpu-info --version")
81+
patterns = ["tpu-info version:", "libtpu version:", "accelerator type:"]
82+
check_output_contains(output, patterns, "tpu-info --version")
11583
return output
11684

11785

118-
def _get_tpu_process_output(info: node_pool.Info, pod_name: str) -> str:
119-
"""
120-
Executes the process command in the pod to get the TPU process table.
121-
"""
122-
with tempfile.NamedTemporaryFile() as temp_config_file:
123-
env = os.environ.copy()
124-
env["KUBECONFIG"] = temp_config_file.name
125-
126-
cmd = " && ".join([
127-
jobset.Command.get_credentials_command(info),
128-
f"kubectl exec {pod_name} -n default -- tpu-info --process",
129-
])
130-
return subprocess.run_exec(cmd, env=env)
131-
132-
13386
@task
134-
def validate_tpu_info_process(info: node_pool.Info, pod_name: str) -> str:
135-
"""
136-
Validates that `tpu-info --process` displays the TPU process table.
137-
"""
138-
output = _get_tpu_process_output(info, pod_name)
139-
required_patterns = [
87+
def validate_process(info, pod_name: str) -> str:
88+
output = run_command(info, pod_name, "tpu-info --process")
89+
patterns = [
14090
"TPU Process Info",
14191
"Chip",
14292
"PID",
14393
"Process Name",
14494
"/dev/vfio/",
14595
"python",
14696
]
147-
148-
for pattern in required_patterns:
149-
if pattern not in output:
150-
raise AssertionError(
151-
f"Validation failed: Missing expected string '{pattern}' "
152-
f"in tpu-info --process output.\nOutput:\n{output}"
153-
)
154-
97+
check_output_contains(output, patterns, "tpu-info --process")
15598
return output
15699

157100

158101
# Keyword arguments are generated dynamically at runtime (pylint does not
159102
# know this signature).
160103
with models.DAG( # pylint: disable=unexpected-keyword-arg
161-
dag_id="tpu_info_help_validation_dags",
104+
dag_id="tpu_info_cli_validation_dags",
162105
start_date=datetime.datetime(2025, 8, 10),
163106
schedule="0 18 * * *" if composer_env.is_prod_env() else None,
164107
catchup=False,
@@ -193,7 +136,7 @@ def validate_tpu_info_process(info: node_pool.Info, pod_name: str) -> str:
193136
config = machine.value
194137

195138
jobset_config = JobSet(
196-
jobset_name="tpu-info-help-validation-jobset",
139+
jobset_name="tpu-info-cli-validation-jobset",
197140
namespace="default",
198141
max_restarts=5,
199142
replicated_job_name="tpu-job-slice",
@@ -218,7 +161,7 @@ def validate_tpu_info_process(info: node_pool.Info, pod_name: str) -> str:
218161
task_id="build_node_pool_info_from_gcs_yaml"
219162
)(
220163
gcs_path=GCS_CONFIG_PATH,
221-
dag_name="tpu_info_help_validation_dags",
164+
dag_name="tpu_info_cli_validation_dags",
222165
is_prod=composer_env.is_prod_env(),
223166
machine_type=config.machine_version.value,
224167
tpu_topology=config.tpu_topology,
@@ -250,17 +193,17 @@ def validate_tpu_info_process(info: node_pool.Info, pod_name: str) -> str:
250193
with TaskGroup( # pylint: disable=unexpected-keyword-arg
251194
group_id="verification_group"
252195
) as verification_group:
253-
validate_help = validate_tpu_info_help.partial(
254-
info=cluster_info
255-
).expand(pod_name=pod_names)
196+
help_validation = validate_help.partial(info=cluster_info).expand(
197+
pod_name=pod_names
198+
)
256199

257-
validate_version = validate_tpu_info_version.partial(
258-
info=cluster_info
259-
).expand(pod_name=pod_names)
200+
version_validation = validate_version.partial(info=cluster_info).expand(
201+
pod_name=pod_names
202+
)
260203

261-
validate_process = validate_tpu_info_process.partial(
262-
info=cluster_info
263-
).expand(pod_name=pod_names)
204+
process_validation = validate_process.partial(info=cluster_info).expand(
205+
pod_name=pod_names
206+
)
264207

265208
cleanup_workload = jobset.end_workload.override(
266209
task_id="cleanup_workload", trigger_rule=TriggerRule.ALL_DONE

0 commit comments

Comments
 (0)