Commit a72f012f authored by one's avatar one
Browse files

[xcl-lens] Improve topo mapping report

parent a4e38ce1
...@@ -113,10 +113,30 @@ class RcclLogParser: ...@@ -113,10 +113,30 @@ class RcclLogParser:
print(f"{field}: {values[0]}") print(f"{field}: {values[0]}")
continue continue
print(f"{field}: (WARNING: Different values across ranks)") host_values = {}
for (host, rank), field_values in sorted(entries.items()): host_has_rank_conflict = set()
joined = " | ".join(sorted(field_values)) for (host, _rank), field_values in entries.items():
print(f" {host} rank {rank}: {joined}") host_values.setdefault(host, set()).update(field_values)
if len(field_values) > 1:
host_has_rank_conflict.add(host)
for host, field_values in host_values.items():
if len(field_values) > 1:
host_has_rank_conflict.add(host)
warning_scope = "nodes" if len(host_values) > 1 else "ranks"
print(f"{field}: (WARNING: Different values across {warning_scope})")
for host in sorted(host_values):
if host not in host_has_rank_conflict:
joined = " | ".join(sorted(host_values[host]))
print(f" {host}: {joined}")
continue
for (entry_host, rank), field_values in sorted(entries.items()):
if entry_host != host:
continue
joined = " | ".join(sorted(field_values))
print(f" {host} rank {rank}: {joined}")
print() print()
def _report_net_ib_info(self): def _report_net_ib_info(self):
...@@ -448,88 +468,81 @@ class RcclLogParser: ...@@ -448,88 +468,81 @@ class RcclLogParser:
print(df.to_string(index=False)) print(df.to_string(index=False))
print() print()
def _report_aggregated_info( def _report_topo_mapping_info(self):
self, print("===> Topology Mapping File Info:\n")
title: str,
filter_func,
patterns: list,
accumulate_fields: set,
col_order: list,
):
"""
Generic method for sections where multiple log lines contribute different
fields to a single per-(host, rank) record.
Unlike _extract_and_print (one line → all fields), this aggregates
across lines: each line may fill one field of the record.
Args:
title: Section title.
filter_func: Pre-filter for log content (content -> bool).
patterns: List of (search_pattern, field, group_idx, literal).
accumulate_fields: Fields that collect values across multiple lines (stored as set).
col_order: Preferred column display order.
"""
print(f"===> {title}:\n")
records: dict[tuple, dict] = {} topo_mapping_patterns = [
(r"No topo mapping file", "status", None, "no_file"),
(r"environmental key word is (\S+)", "fingerprint", 1, None),
(r"Loading topology mapping file (\S+)", "loaded", 1, None),
(r"(?:parseing|parsing) topology mapping group[:\s]*(.*)", "parsed", 1, None),
(r"skip topology mapping group:\s*([^,]+)", "skipped", 1, None),
]
records = {}
for host, rank, content in self.log_entries: for host, rank, content in self.log_entries:
if not filter_func(content): if not any(
s in content.lower()
for s in ("topo mapping", "topology mapping", "environmental key word")
):
continue continue
for pattern, field, group_idx, literal in patterns: for pattern, field, group_idx, literal in topo_mapping_patterns:
m = re.search(pattern, content, re.IGNORECASE) m = re.search(pattern, content, re.IGNORECASE)
if m: if not m:
key = (host, rank) continue
rec = records.setdefault(key, {"host": host, "rank": rank}) rec = records.setdefault((host, rank), {"host": host, "rank": rank})
value = (literal if group_idx is None else m.group(group_idx).strip()) or "-" value = (literal if group_idx is None else m.group(group_idx).strip()) or "-"
if field in accumulate_fields: if field in {"parsed", "skipped"}:
rec.setdefault(field, set()).add(value) rec.setdefault(field, set()).add(value)
else: else:
rec[field] = value rec[field] = value
break # each line matches at most one pattern break
if not records: if not records:
print(" (No data found)\n") print(" (No data found)\n")
return return
# Flatten accumulated sets → sorted string
for rec in records.values(): for rec in records.values():
for field in accumulate_fields: for field in ("parsed", "skipped"):
if field in rec: if field in rec:
rec[field] = " | ".join(sorted(rec[field])) rec[field] = sorted(rec[field])
if rec.get("status") == "no_file":
df = pd.DataFrame(list(records.values())) continue
df.sort_values(by=["host", "rank"], inplace=True) if rec.get("parsed"):
rec["status"] = "parsed"
ordered = [c for c in col_order if c in df.columns] elif rec.get("skipped"):
remaining = [c for c in df.columns if c not in ordered] rec["status"] = "skipped"
df = df[ordered + remaining] else:
rec["status"] = "-"
print(df.fillna("-").to_string(index=False))
print() by_host = {}
for (host, rank), rec in records.items():
by_host.setdefault(host, []).append((rank, rec))
field_order = ["status", "fingerprint", "loaded", "parsed", "skipped"]
for host in sorted(by_host):
print(host)
host_records = sorted(by_host[host], key=lambda item: item[0])
normalized = [
{field: rec.get(field, "-") for field in field_order} for _, rec in host_records
]
if len({repr(item) for item in normalized}) == 1:
self._print_topo_record(normalized[0], indent=" ")
print()
continue
def _report_topo_mapping_info(self): for rank, rec in host_records:
# (search_pattern, field_name, capture_group_index_or_None, literal_value_or_None) print(f" rank {rank}")
# - capture_group_index: int → get regex group, None → use literal_value self._print_topo_record(rec, indent=" ")
# - accumulate: True → this field may come from multiple lines, append rather than overwrite print()
topo_mapping_patterns = [
(r"No topo mapping file", "status", None, "no_file"),
(r"environmental key word is (\S+)", "fingerprint", 1, None),
(r"Loading topology mapping file (\S+)", "loaded", 1, None),
(r"(?:parseing|parsing) topology mapping group[:\s]*(.*)", "parsed", 1, None),
(r"skip topology mapping group:\s*([^,]+)", "skipped", 1, None),
]
# Fields that should accumulate across multiple matching lines (per host/rank)
topo_mapping_accumulate_fields = {"skipped", "parsed"}
self._report_aggregated_info( def _print_topo_record(self, record, indent):
title="Topology Mapping File Info", for field in ("status", "fingerprint", "loaded"):
filter_func=lambda c: any( if field in record:
s in c.lower() print(f"{indent}{field}: {record[field]}")
for s in ("topo mapping", "topology mapping", "environmental key word") for field in ("parsed", "skipped"):
), if field not in record:
patterns=topo_mapping_patterns, continue
accumulate_fields=topo_mapping_accumulate_fields, print(f"{indent}{field}:")
col_order=["host", "rank", "status", "fingerprint", "loaded", "parsed", "skipped"], for value in record[field]:
) print(f"{indent} {value}")
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment