Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions docs/reference/api-full.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ For a curated, example-driven API guide, see **[api.md](api.md)**.
> - **[CLI Reference](cli.md)** - Command-line interface
> - **[DSL Reference](dsl.md)** - YAML syntax guide

**Generated from source code on:** August 06, 2025 at 16:18 UTC
**Generated from source code on:** August 06, 2025 at 21:33 UTC

**Modules auto-discovered:** 53

Expand Down Expand Up @@ -432,7 +432,6 @@ Example YAML configuration:
```yaml
failure_policy:
attrs:
name: "Texas Grid Outage Scenario"
description: "Regional power grid failure affecting telecom infrastructure"
fail_risk_groups: true
rules:
Expand Down
1 change: 0 additions & 1 deletion docs/reference/dsl.md
Original file line number Diff line number Diff line change
Expand Up @@ -411,7 +411,6 @@ Defines named failure policies for simulating network failures to test resilienc
```yaml
failure_policy_set:
policy_name_1:
name: "PolicyName" # Optional
fail_risk_groups: true | false
fail_risk_group_children: true | false
use_cache: true | false
Expand Down
4 changes: 3 additions & 1 deletion ngraph/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,9 @@ def _inspect_scenario(path: Path, detail: bool = False) -> None:
original_level = logger.level
logger.setLevel(logging.WARNING)
try:
explorer = NetworkExplorer.explore_network(network)
explorer = NetworkExplorer.explore_network(
network, scenario.components_library
)
print("\n Network Hierarchy:")
explorer.print_tree(
max_depth=3 if not detail else None,
Expand Down
1 change: 0 additions & 1 deletion ngraph/failure_policy.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,6 @@ class FailurePolicy:
```yaml
failure_policy:
attrs:
name: "Texas Grid Outage Scenario"
description: "Regional power grid failure affecting telecom infrastructure"
fail_risk_groups: true
rules:
Expand Down
1 change: 0 additions & 1 deletion ngraph/scenario.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,6 @@ def _build_failure_policy(
Example:
failure_policy_set:
default:
name: "test" # (Currently unused if present)
fail_risk_groups: true
fail_risk_group_children: false
use_cache: true
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
# ---------------------------------------------------------------------
[project]
name = "ngraph"
version = "0.8.0"
version = "0.8.1"
description = "A tool and a library for network modeling and capacity analysis."
readme = "README.md"
authors = [{ name = "Andrey Golovanov" }]
Expand Down
8 changes: 3 additions & 5 deletions scenarios/nsfnet.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,6 @@ risk_groups:
failure_policy_set:
availability_1992:
attrs:
name: "historical_availability_1992"
description: >
Approximates 1992 backbone reliability: each physical DS-3 has
~99.9 % monthly availability (p=0.001 failure), and each CNSS or
Expand All @@ -174,9 +173,8 @@ failure_policy_set:
rule_type: random
probability: 0.0005 # 0.05 % chance a given node is down

default:
single_random_link_failure:
attrs:
name: single_random_link_failure
description: Fails exactly one random link to test network resilience
rules:
- entity_scope: link
Expand All @@ -199,7 +197,7 @@ workflow:
flow_placement: PROPORTIONAL
iterations: 1000
baseline: true
failure_policy: default
failure_policy: single_random_link_failure
store_failure_patterns: true
- step_type: CapacityEnvelopeAnalysis
name: ce_2
Expand All @@ -209,7 +207,7 @@ workflow:
parallelism: 8
shortest_path: false
flow_placement: PROPORTIONAL
iterations: 1000000
iterations: 10000
baseline: true
failure_policy: availability_1992
store_failure_patterns: true
6 changes: 2 additions & 4 deletions scenarios/simple.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -111,17 +111,15 @@ risk_groups:
- name: srlg_12

failure_policy_set:
default:
single_random_link_failure:
attrs:
name: "single_random_link_failure"
description: "Fails exactly one random link to test network resilience"
rules:
- entity_scope: "link"
rule_type: "choice"
count: 1
single_shared_risk_group_failure:
attrs:
name: "single_shared_risk_group_failure"
description: "Fails exactly one random shared risk group to test network resilience"
rules:
- entity_scope: "risk_group"
Expand All @@ -144,7 +142,7 @@ workflow:
seed: 42
iterations: 1000
baseline: true # Enable baseline mode
failure_policy: "default"
failure_policy: "single_random_link_failure"
- step_type: CapacityEnvelopeAnalysis
name: "ce_2"
source_path: "^(spoke_.+)"
Expand Down
6 changes: 3 additions & 3 deletions tests/integration/expectations.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,9 @@ def _calculate_scenario_3_total_nodes() -> int:

# Failure policy expectations by scenario
FAILURE_POLICY_EXPECTATIONS = {
"scenario_1": {"name": "anySingleLink", "rules": 1, "scopes": ["link"]},
"scenario_2": {"name": "anySingleLink", "rules": 1, "scopes": ["link"]},
"scenario_3": {"name": None, "rules": 0, "scopes": []}, # No failure policy
"scenario_1": {"rules": 1, "scopes": ["link"]},
"scenario_2": {"rules": 1, "scopes": ["link"]},
"scenario_3": {"rules": 0, "scopes": []}, # No failure policy
}

# Scenario 4: Advanced DSL features with complex data center fabric
Expand Down
21 changes: 6 additions & 15 deletions tests/integration/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -251,15 +251,13 @@ def validate_traffic_demands(self, expected_count: int) -> None:

def validate_failure_policy(
self,
expected_name: Optional[str],
expected_rules: int,
expected_scopes: Optional[List[str]] = None,
) -> None:
"""
Validate failure policy configuration.

Args:
expected_name: Expected failure policy name (None if no policy expected)
expected_rules: Expected number of failure rules
expected_scopes: Optional list of expected rule scopes (node/link)

Expand All @@ -268,26 +266,19 @@ def validate_failure_policy(
"""
policy = self.scenario.failure_policy_set.get_default_policy()

if expected_name is None:
assert policy is None, (
f"Expected no default failure policy, but found: {policy.attrs.get('name') if policy else None}"
if policy is None:
# No policy exists - only valid if expecting zero rules
assert expected_rules == 0, (
f"Expected a failure policy with {expected_rules} rules, but no default policy found"
)
return

assert policy is not None, "Expected a default failure policy but none found"

# Validate rule count
# Policy exists - validate rule count
actual_rules = len(policy.rules)
assert actual_rules == expected_rules, (
f"Failure policy rule count mismatch: expected {expected_rules}, found {actual_rules}"
)

# Validate policy name
actual_name = policy.attrs.get("name")
assert actual_name == expected_name, (
f"Failure policy name mismatch: expected '{expected_name}', found '{actual_name}'"
)

# Validate rule scopes if specified
if expected_scopes:
actual_scopes = [rule.entity_scope for rule in policy.rules]
Expand Down Expand Up @@ -763,7 +754,7 @@ def basic_failure_scenario() -> Scenario:
.with_failure_policy(
"single_link_failure",
{
"attrs": {"name": "single_link", "description": "Single link failure"},
"attrs": {"description": "Single link failure"},
"rules": [{"entity_scope": "link", "rule_type": "choice", "count": 1}],
},
)
Expand Down
1 change: 0 additions & 1 deletion tests/integration/scenario_1.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,6 @@ network:
failure_policy_set:
default:
attrs:
name: "anySingleLink"
description: "Evaluate traffic routing under any single link failure."
rules:
- entity_scope: "link"
Expand Down
1 change: 0 additions & 1 deletion tests/integration/scenario_2.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,6 @@ network:
failure_policy_set:
default:
attrs:
name: "anySingleLink"
description: "Evaluate traffic routing under any single link failure."
rules:
- entity_scope: "link"
Expand Down
3 changes: 0 additions & 3 deletions tests/integration/scenario_4.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,6 @@ traffic_matrix_set:
failure_policy_set:
single_link_failure:
attrs:
name: "single_link_failure"
description: "Single link failure"
rules:
- entity_scope: "link"
Expand All @@ -285,7 +284,6 @@ failure_policy_set:

single_node_failure:
attrs:
name: "single_node_failure"
description: "Single node failure"
rules:
- entity_scope: "node"
Expand All @@ -294,7 +292,6 @@ failure_policy_set:

default:
attrs:
name: "random_link_failure"
description: "Random single link failure"
rules:
- entity_scope: "link"
Expand Down
5 changes: 0 additions & 5 deletions tests/integration/test_data_templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,6 @@ def single_link_failure() -> Dict[str, Any]:
"""Template for single link failure policy."""
return {
"attrs": {
"name": "single_link_failure",
"description": "Single link failure scenario",
},
"rules": [{"entity_scope": "link", "rule_type": "choice", "count": 1}],
Expand All @@ -272,7 +271,6 @@ def single_node_failure() -> Dict[str, Any]:
"""Template for single node failure policy."""
return {
"attrs": {
"name": "single_node_failure",
"description": "Single node failure scenario",
},
"rules": [{"entity_scope": "node", "rule_type": "choice", "count": 1}],
Expand All @@ -283,7 +281,6 @@ def multiple_failure(entity_scope: str, count: int) -> Dict[str, Any]:
"""Template for multiple simultaneous failures."""
return {
"attrs": {
"name": f"multiple_{entity_scope}_failure",
"description": f"Multiple {entity_scope} failure scenario",
},
"rules": [
Expand All @@ -296,7 +293,6 @@ def all_links_failure() -> Dict[str, Any]:
"""Template for all links failure policy."""
return {
"attrs": {
"name": "all_links_failure",
"description": "All links failure scenario",
},
"rules": [{"entity_scope": "link", "rule_type": "all"}],
Expand All @@ -307,7 +303,6 @@ def risk_group_failure(risk_group_name: str) -> Dict[str, Any]:
"""Template for risk group-based failure policy."""
return {
"attrs": {
"name": f"{risk_group_name}_failure",
"description": f"Failure of risk group {risk_group_name}",
},
"fail_risk_groups": True,
Expand Down
6 changes: 2 additions & 4 deletions tests/integration/test_scenario_1.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,9 +161,7 @@ def test_traffic_demands_configuration(self, helper):

def test_failure_policy_configuration(self, helper):
"""Test that failure policy is correctly configured."""
helper.validate_failure_policy(
expected_name="anySingleLink", expected_rules=1, expected_scopes=["link"]
)
helper.validate_failure_policy(expected_rules=1, expected_scopes=["link"])

# Additional validation of the specific rule
policy = helper.scenario.failure_policy_set.get_default_policy()
Expand Down Expand Up @@ -246,4 +244,4 @@ def test_scenario_1_build_graph():
# Basic validation using helper
helper.validate_network_structure(SCENARIO_1_EXPECTATIONS)
helper.validate_traffic_demands(4)
helper.validate_failure_policy("anySingleLink", 1, ["link"])
helper.validate_failure_policy(1, ["link"])
6 changes: 2 additions & 4 deletions tests/integration/test_scenario_2.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,9 +228,7 @@ def test_traffic_demands_configuration(self, helper):

def test_failure_policy_configuration(self, helper):
"""Test failure policy configuration."""
helper.validate_failure_policy(
expected_name="anySingleLink", expected_rules=1, expected_scopes=["link"]
)
helper.validate_failure_policy(expected_rules=1, expected_scopes=["link"])

def test_topology_semantic_correctness(self, helper):
"""Test that the expanded network topology is semantically correct."""
Expand Down Expand Up @@ -282,4 +280,4 @@ def test_scenario_2_build_graph():
# Basic validation using helper
helper.validate_network_structure(SCENARIO_2_EXPECTATIONS)
helper.validate_traffic_demands(4)
helper.validate_failure_policy("anySingleLink", 1, ["link"])
helper.validate_failure_policy(1, ["link"])
4 changes: 2 additions & 2 deletions tests/integration/test_scenario_3.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ def test_no_traffic_demands(self, helper):

def test_no_failure_policy(self, helper):
"""Test that this scenario has no failure policy as expected."""
helper.validate_failure_policy(expected_name=None, expected_rules=0)
helper.validate_failure_policy(expected_rules=0)

def test_capacity_probe_proportional_flow_results(self, helper):
"""Test capacity probe results with PROPORTIONAL flow placement."""
Expand Down Expand Up @@ -335,7 +335,7 @@ def test_scenario_3_build_graph_and_capacity_probe():
# Basic validation using helper
helper.validate_network_structure(SCENARIO_3_EXPECTATIONS)
helper.validate_traffic_demands(0)
helper.validate_failure_policy(None, 0)
helper.validate_failure_policy(0)

# Validate key flow results
helper.validate_flow_results(
Expand Down
6 changes: 3 additions & 3 deletions tests/integration/test_template_examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ def test_single_link_failure_template(self):
"""Test single link failure policy template."""
policy = FailurePolicyTemplates.single_link_failure()

assert policy["attrs"]["name"] == "single_link_failure"
assert "name" not in policy["attrs"]
assert len(policy["rules"]) == 1

rule = policy["rules"][0]
Expand All @@ -171,7 +171,7 @@ def test_multiple_failure_template(self):
"""Test multiple failure policy template."""
policy = FailurePolicyTemplates.multiple_failure("node", 3)

assert policy["attrs"]["name"] == "multiple_node_failure"
assert "name" not in policy["attrs"]
assert len(policy["rules"]) == 1

rule = policy["rules"][0]
Expand All @@ -182,7 +182,7 @@ def test_risk_group_failure_template(self):
"""Test risk group failure policy template."""
policy = FailurePolicyTemplates.risk_group_failure("datacenter_a")

assert policy["attrs"]["name"] == "datacenter_a_failure"
assert "name" not in policy["attrs"]
assert policy["fail_risk_groups"] is True
assert len(policy["rules"]) == 1

Expand Down
Loading