2828
2929from airflow .decorators import task
3030from airflow .exceptions import AirflowFailException
31+ from airflow .sensors .base import PokeReturnValue
3132from google .cloud .monitoring_v3 import types
3233import kubernetes
3334
3839from dags .tpu_observability .utils .node_pool_util import NODE_POOL_SELECTOR_KEY
3940from dags .tpu_observability .utils .time_util import TimeUtil
4041from xlml .apis import gcs
42+ from xlml .utils import composer
4143from xlml .utils import gke
4244
4345
@@ -507,6 +509,24 @@ def run_workload(
507509
508510 subprocess .run_exec (cmd , env = env )
509511
512+ # Log metadata for XLML dashboard
513+ # Pod names follow the pattern:
514+ # {jobset_name}-{replicated_job_name}-{job-index}-{pod-index}-{random}
515+ # The jobset_name prefix is stable across pod recreations, so a regex
516+ # pattern is more reliable than an exact pod name list.
517+ pod_name_pattern = f"{ jobset_config .jobset_name } .*"
518+ jobset_metadata = {
519+ "project_id" : node_pool .project_id ,
520+ "cluster_name" : node_pool .cluster_name ,
521+ "node_pool_name" : node_pool .node_pool_name ,
522+ "jobset_name" : jobset_config .jobset_name ,
523+ "pod_name_pattern" : pod_name_pattern ,
524+ }
525+ composer .log_metadata_for_xlml_dashboard (jobset_metadata )
526+ logging .info (
527+ "Logged JobSet metadata to XLML dashboard: %s" , jobset_metadata
528+ )
529+
510530 current_time_utc = datetime .datetime .now (datetime .timezone .utc )
511531 return TimeUtil .from_datetime (current_time_utc )
512532
@@ -724,7 +744,8 @@ def wait_for_jobset_ttr_to_be_found(
724744
725745 Args:
726746 node_pool (Info): An instance of the Info class containing GKE metadata.
727- jobset_config: An instance of the JobSet class representing the jobset configuration.
747+ jobset_config: An instance of the JobSet class representing the jobset
748+ configuration.
728749 start_time (TimeUtil, optional): The UTC timestamp to start polling from.
729750 If not provided, defaults to 60 minutes before the current time.
730751
@@ -749,23 +770,39 @@ def wait_for_jobset_ttr_to_be_found(
749770 end_time = TimeUtil .from_datetime (now ),
750771 )
751772
752- # This function checks whether the TTR metric is present;
753- # it does not assess its value.
754773 logging .info ("Time series: %s" , time_series )
755774 return len (time_series ) > 0
756775
757776
758777@task .sensor (poke_interval = 30 , timeout = 600 , mode = "poke" )
759- def wait_for_all_pods_running (node_pool : node_pool_info , jobset_config : JobSet ):
760- num_running = len (
761- get_running_pods (
762- node_pool = node_pool ,
763- jobset_name = jobset_config .jobset_name ,
764- namespace = "default" ,
765- )
778+ def wait_for_all_pods_running (
779+ node_pool : node_pool_info , jobset_config : JobSet
780+ ) -> PokeReturnValue :
781+ """Waits for all pods to be running and returns the pod names.
782+
783+ Args:
784+ node_pool: The Info object containing the cluster information.
785+ jobset_config: The JobSet configuration.
786+
787+ Returns:
788+ PokeReturnValue with is_done=True and pod names when all pods are running,
789+ or is_done=False to continue polling.
790+ """
791+ running_pods = get_running_pods (
792+ node_pool = node_pool ,
793+ jobset_name = jobset_config .jobset_name ,
794+ namespace = "default" ,
766795 )
767796 num_pods = jobset_config .replicas * jobset_config .parallelism
768- return num_running == num_pods
797+ if len (running_pods ) == num_pods :
798+ logging .info (
799+ "All %d pods are running for JobSet '%s': %s" ,
800+ num_pods ,
801+ jobset_config .jobset_name ,
802+ running_pods ,
803+ )
804+ return PokeReturnValue (is_done = True , xcom_value = running_pods )
805+ return PokeReturnValue (is_done = False )
769806
770807
771808def query_uptime_metrics (
0 commit comments