Skip to content

Commit ea43e33

Browse files
Andrey Golovanovclaude
andcommitted
Fix risk group assignments on DC-BB and BB cross-site links
Two bugs fixed: 1. DC-BB links (FADU↔BB, XSW↔BB) had no risk_groups — correlated failures didn't affect DC-BB connectivity. Now each link carries plane_site, plane_group, and device-index-across-PG risk groups from its BB endpoint. 2. BB cross-site links lacked device-index risk groups. Now each link carries pg_G_idx_D_abc1 and pg_G_idx_D_xyz1 from both endpoints. Links went from 4 to 6 risk groups. Full audit confirmed all other topology aspects match spec. 430 tests pass. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 72cb56d commit ea43e33

4 files changed

Lines changed: 190 additions & 5 deletions

File tree

netlab/autoresearch/scenario_generator.py

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,7 @@ def _build_bb_cross_site_links(config: DcBbScenarioConfig) -> list[dict]:
516516
"""
517517
links: list[dict] = []
518518
for pl in range(1, config.bb_planes + 1):
519+
pg = (pl - 1) // 4 + 1
519520
for a in range(1, config.bb_devices_per_plane + 1):
520521
for x in range(1, config.bb_devices_per_plane + 1):
521522
for path in ["path_a", "path_b"]:
@@ -527,9 +528,11 @@ def _build_bb_cross_site_links(config: DcBbScenarioConfig) -> list[dict]:
527528
"cost": 10,
528529
"risk_groups": [
529530
path,
530-
f"plane_group_{(pl - 1) // 4 + 1}",
531+
f"plane_group_{pg}",
531532
f"plane_{pl}_site_abc1",
532533
f"plane_{pl}_site_xyz1",
534+
f"pg_{pg}_idx_{a}_abc1",
535+
f"pg_{pg}_idx_{x}_xyz1",
533536
],
534537
"attrs": {
535538
"link_type": "bb_cross_site",
@@ -582,13 +585,21 @@ def _build_dc_bb_links(config: DcBbScenarioConfig) -> list[dict]:
582585
for dr, dc in dc_devs:
583586
fadu_name = f"abc1/fadu/hgrid{dr + 1}/idx{dc + 1}"
584587
for br, bc in bb_devs:
585-
bb_name = f"bb/abc1/plane{br + 1}/dev{bc + 1}"
588+
bb_plane = br + 1
589+
bb_dev = bc + 1
590+
bb_name = f"bb/abc1/plane{bb_plane}/dev{bb_dev}"
591+
pg = (bb_plane - 1) // 4 + 1
586592
links.append(
587593
{
588594
"source": fadu_name,
589595
"target": bb_name,
590596
"capacity": config.dc_bb_link_capacity,
591597
"cost": 5,
598+
"risk_groups": [
599+
f"plane_{bb_plane}_site_abc1",
600+
f"plane_group_{pg}",
601+
f"pg_{pg}_idx_{bb_dev}_abc1",
602+
],
592603
"attrs": {"link_type": "dc_bb", "side": "abc1"},
593604
}
594605
)
@@ -609,13 +620,21 @@ def _build_dc_bb_links(config: DcBbScenarioConfig) -> list[dict]:
609620
# dr = device index within plane, dc = plane index
610621
xsw_name = f"xyz1/xsw/plane{dc + 1}/dev{dr + 1}"
611622
for br, bc in bb_devs:
612-
bb_name = f"bb/xyz1/plane{br + 1}/dev{bc + 1}"
623+
bb_plane = br + 1
624+
bb_dev = bc + 1
625+
bb_name = f"bb/xyz1/plane{bb_plane}/dev{bb_dev}"
626+
pg = (bb_plane - 1) // 4 + 1
613627
links.append(
614628
{
615629
"source": xsw_name,
616630
"target": bb_name,
617631
"capacity": config.dc_bb_link_capacity,
618632
"cost": 5,
633+
"risk_groups": [
634+
f"plane_{bb_plane}_site_xyz1",
635+
f"plane_group_{pg}",
636+
f"pg_{pg}_idx_{bb_dev}_xyz1",
637+
],
619638
"attrs": {"link_type": "dc_bb", "side": "xyz1"},
620639
}
621640
)

tests/autoresearch/test_bb_links.py

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,9 @@ class TestRiskGroups:
8484
"""Verify risk group assignments."""
8585

8686
def test_risk_group_structure(self, default_links):
87-
"""Every link has exactly 4 risk groups."""
87+
"""Every link has exactly 6 risk groups."""
8888
for link in default_links:
89-
assert len(link["risk_groups"]) == 4
89+
assert len(link["risk_groups"]) == 6
9090

9191
def test_risk_group_contents(self, default_links):
9292
"""Spot-check a specific link's risk groups."""
@@ -106,6 +106,8 @@ def test_risk_group_contents(self, default_links):
106106
"plane_group_1",
107107
"plane_1_site_abc1",
108108
"plane_1_site_xyz1",
109+
"pg_1_idx_1_abc1",
110+
"pg_1_idx_1_xyz1",
109111
]
110112

111113
def test_plane_group_mapping(self, default_links):
@@ -145,6 +147,31 @@ def test_16_plane_groups_total(self, default_links):
145147
assert len(groups) == 16
146148
assert groups == {f"plane_group_{g}" for g in range(1, 17)}
147149

150+
def test_device_index_risk_groups_present(self, default_links):
151+
"""Each link includes pg_G_idx_D_abc1 and pg_G_idx_D_xyz1."""
152+
for link in default_links:
153+
src_dev = int(link["source"].split("/")[-1].replace("dev", ""))
154+
tgt_dev = int(link["target"].split("/")[-1].replace("dev", ""))
155+
pl = link["attrs"]["plane"]
156+
pg = (pl - 1) // 4 + 1
157+
assert f"pg_{pg}_idx_{src_dev}_abc1" in link["risk_groups"]
158+
assert f"pg_{pg}_idx_{tgt_dev}_xyz1" in link["risk_groups"]
159+
160+
def test_device_index_spot_check(self, default_links):
161+
"""Spot-check: plane7 dev2->dev3 link should have pg_2_idx_2_abc1, pg_2_idx_3_xyz1."""
162+
target = None
163+
for link in default_links:
164+
if (
165+
link["source"] == "bb/abc1/plane7/dev2"
166+
and link["target"] == "bb/xyz1/plane7/dev3"
167+
and link["attrs"]["path"] == "path_a"
168+
):
169+
target = link
170+
break
171+
assert target is not None
172+
assert "pg_2_idx_2_abc1" in target["risk_groups"]
173+
assert "pg_2_idx_3_xyz1" in target["risk_groups"]
174+
148175

149176
# ---------------------------------------------------------------------------
150177
# Link attributes

tests/autoresearch/test_dc_bb_links.py

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,101 @@ def test_side_values(self, default_links):
9595
sides = {lk["attrs"]["side"] for lk in default_links}
9696
assert sides == {"abc1", "xyz1"}
9797

98+
def test_all_links_have_risk_groups(self, default_links):
99+
for lk in default_links:
100+
assert "risk_groups" in lk, (
101+
f"Link {lk['source']}->{lk['target']} missing risk_groups"
102+
)
103+
assert len(lk["risk_groups"]) == 3
104+
105+
106+
# ---------------------------------------------------------------------------
107+
# Risk group assignments on DC-BB links
108+
# ---------------------------------------------------------------------------
109+
110+
111+
class TestDcBbRiskGroups:
112+
"""Verify risk group assignments derived from BB endpoint."""
113+
114+
def test_abc1_link_has_plane_site_group(self, default_links):
115+
"""ABC1 DC-BB links include plane_P_site_abc1 from BB endpoint."""
116+
abc1 = [lk for lk in default_links if lk["attrs"]["side"] == "abc1"]
117+
for lk in abc1:
118+
# Extract plane from target name bb/abc1/plane{P}/dev{D}
119+
parts = lk["target"].split("/")
120+
plane = int(parts[2].replace("plane", ""))
121+
expected_rg = f"plane_{plane}_site_abc1"
122+
assert expected_rg in lk["risk_groups"], (
123+
f"{lk['target']}: expected {expected_rg} in {lk['risk_groups']}"
124+
)
125+
126+
def test_xyz1_link_has_plane_site_group(self, default_links):
127+
"""XYZ1 DC-BB links include plane_P_site_xyz1 from BB endpoint."""
128+
xyz1 = [lk for lk in default_links if lk["attrs"]["side"] == "xyz1"]
129+
for lk in xyz1:
130+
parts = lk["target"].split("/")
131+
plane = int(parts[2].replace("plane", ""))
132+
expected_rg = f"plane_{plane}_site_xyz1"
133+
assert expected_rg in lk["risk_groups"], (
134+
f"{lk['target']}: expected {expected_rg} in {lk['risk_groups']}"
135+
)
136+
137+
def test_abc1_link_has_plane_group(self, default_links):
138+
"""ABC1 DC-BB links include plane_group_G from BB endpoint's plane."""
139+
abc1 = [lk for lk in default_links if lk["attrs"]["side"] == "abc1"]
140+
for lk in abc1:
141+
parts = lk["target"].split("/")
142+
plane = int(parts[2].replace("plane", ""))
143+
pg = (plane - 1) // 4 + 1
144+
expected_rg = f"plane_group_{pg}"
145+
assert expected_rg in lk["risk_groups"]
146+
147+
def test_abc1_link_has_device_index_group(self, default_links):
148+
"""ABC1 DC-BB links include pg_G_idx_D_abc1 from BB endpoint."""
149+
abc1 = [lk for lk in default_links if lk["attrs"]["side"] == "abc1"]
150+
for lk in abc1:
151+
parts = lk["target"].split("/")
152+
plane = int(parts[2].replace("plane", ""))
153+
dev = int(parts[3].replace("dev", ""))
154+
pg = (plane - 1) // 4 + 1
155+
expected_rg = f"pg_{pg}_idx_{dev}_abc1"
156+
assert expected_rg in lk["risk_groups"]
157+
158+
def test_xyz1_link_has_device_index_group(self, default_links):
159+
"""XYZ1 DC-BB links include pg_G_idx_D_xyz1 from BB endpoint."""
160+
xyz1 = [lk for lk in default_links if lk["attrs"]["side"] == "xyz1"]
161+
for lk in xyz1:
162+
parts = lk["target"].split("/")
163+
plane = int(parts[2].replace("plane", ""))
164+
dev = int(parts[3].replace("dev", ""))
165+
pg = (plane - 1) // 4 + 1
166+
expected_rg = f"pg_{pg}_idx_{dev}_xyz1"
167+
assert expected_rg in lk["risk_groups"]
168+
169+
def test_spot_check_specific_abc1_link(self, default_links):
170+
"""Spot-check: FADU->bb/abc1/plane7/dev2 should have specific risk groups."""
171+
abc1 = [lk for lk in default_links if lk["attrs"]["side"] == "abc1"]
172+
target_links = [lk for lk in abc1 if lk["target"] == "bb/abc1/plane7/dev2"]
173+
assert len(target_links) > 0, "No links to bb/abc1/plane7/dev2"
174+
for lk in target_links:
175+
assert lk["risk_groups"] == [
176+
"plane_7_site_abc1",
177+
"plane_group_2",
178+
"pg_2_idx_2_abc1",
179+
]
180+
181+
def test_spot_check_specific_xyz1_link(self, default_links):
182+
"""Spot-check: XSW->bb/xyz1/plane7/dev3 should have specific risk groups."""
183+
xyz1 = [lk for lk in default_links if lk["attrs"]["side"] == "xyz1"]
184+
target_links = [lk for lk in xyz1 if lk["target"] == "bb/xyz1/plane7/dev3"]
185+
assert len(target_links) > 0, "No links to bb/xyz1/plane7/dev3"
186+
for lk in target_links:
187+
assert lk["risk_groups"] == [
188+
"plane_7_site_xyz1",
189+
"plane_group_2",
190+
"pg_2_idx_3_xyz1",
191+
]
192+
98193

99194
# ---------------------------------------------------------------------------
100195
# Node name validity

tests/autoresearch/test_risk_groups.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from netlab.autoresearch.scenario_generator import (
88
DcBbScenarioConfig,
99
_build_bb_cross_site_links,
10+
_build_dc_bb_links,
1011
_build_risk_groups,
1112
)
1213

@@ -227,6 +228,49 @@ def test_plane_site_names_in_links(self, default_config):
227228
defined_ps = {g["name"] for g in groups if g["attrs"]["type"] == "plane_site"}
228229
assert ps_names.issubset(defined_ps)
229230

231+
def test_device_index_groups_referenced_by_bb_links(self, default_config):
232+
"""All 128 device_index_across_planes groups are referenced by BB cross-site links."""
233+
groups = _build_risk_groups(default_config)
234+
defined_di = {
235+
g["name"]
236+
for g in groups
237+
if g["attrs"]["type"] == "device_index_across_planes"
238+
}
239+
links = _build_bb_cross_site_links(default_config)
240+
referenced_di = {
241+
rg for link in links for rg in link["risk_groups"] if rg.startswith("pg_")
242+
}
243+
assert defined_di == referenced_di
244+
245+
def test_device_index_groups_referenced_by_dc_bb_links(self, default_config):
246+
"""DC-BB links reference device_index_across_planes groups."""
247+
groups = _build_risk_groups(default_config)
248+
defined_di = {
249+
g["name"]
250+
for g in groups
251+
if g["attrs"]["type"] == "device_index_across_planes"
252+
}
253+
links = _build_dc_bb_links(default_config)
254+
referenced_di = {
255+
rg for link in links for rg in link["risk_groups"] if rg.startswith("pg_")
256+
}
257+
# DC-BB links reference a subset (only one site per link)
258+
assert referenced_di.issubset(defined_di)
259+
assert len(referenced_di) > 0
260+
261+
def test_all_dc_bb_link_risk_groups_defined(self, default_config):
262+
"""Every risk group referenced by DC-BB links exists in the risk group list."""
263+
groups = _build_risk_groups(default_config)
264+
group_names = {g["name"] for g in groups}
265+
links = _build_dc_bb_links(default_config)
266+
referenced = set()
267+
for link in links:
268+
referenced.update(link.get("risk_groups", []))
269+
missing = referenced - group_names
270+
assert not missing, (
271+
f"Risk groups referenced by DC-BB links but not defined: {missing}"
272+
)
273+
230274

231275
# ---------------------------------------------------------------------------
232276
# Custom config

0 commit comments

Comments
 (0)