Skip to content

Commit 5404c08

Browse files
authored
Add vars section to YAML schema and documentation (#88)
1 parent a725f2d commit 5404c08

File tree

6 files changed

+137
-1
lines changed

6 files changed

+137
-1
lines changed

docs/reference/api-full.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ For a curated, example-driven API guide, see **[api.md](api.md)**.
1010
> - **[CLI Reference](cli.md)** - Command-line interface
1111
> - **[DSL Reference](dsl.md)** - YAML syntax guide
1212
13-
**Generated from source code on:** August 06, 2025 at 21:33 UTC
13+
**Generated from source code on:** August 07, 2025 at 00:14 UTC
1414

1515
**Modules auto-discovered:** 53
1616

docs/reference/dsl.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ The scenario YAML file is organized around a **core foundation** that defines yo
1616

1717
The main sections of a scenario YAML file work together to define a complete network simulation:
1818

19+
- `vars`: **[Optional]** Defines YAML anchors and variables for reuse throughout the scenario file.
1920
- `network`: **[Required]** Describes the actual network topology - nodes, links, and their connections.
2021
- `blueprints`: **[Optional]** Defines reusable network templates that can be instantiated multiple times within the network.
2122
- `components`: **[Optional]** A library of hardware and optics definitions with attributes like power consumption.
@@ -442,6 +443,37 @@ failure_policy_set:
442443
- If only one policy exists and no `default` is specified, that policy becomes the default
443444
- Multiple policies allow testing different failure scenarios in the same network
444445

446+
## `vars` - YAML Anchors and Variables
447+
448+
The `vars` section provides a designated space for YAML anchor definitions. YAML anchors (`&name`) and aliases (`*name`) follow the YAML 1.1 specification and are processed by PyYAML during parsing, before NetGraph validation.
449+
450+
**Anchor Types:**
451+
452+
- **Scalar anchors**: Reference primitive values (strings, numbers, booleans)
453+
- **Sequence anchors**: Reference arrays/lists
454+
- **Mapping anchors**: Reference objects/dictionaries
455+
- **Merge keys (`<<`)**: Merge mapping properties with override capability
456+
457+
**Minimal Example:**
458+
459+
```yaml
460+
vars:
461+
default_cap: &cap 10000
462+
base_attrs: &attrs {cost: 100, region: "dc1"}
463+
464+
network:
465+
nodes:
466+
N1: {attrs: {<<: *attrs, capacity: *cap}}
467+
N2: {attrs: {<<: *attrs, capacity: *cap, region: "dc2"}}
468+
```
469+
470+
**Processing Behavior:**
471+
472+
- Anchors are resolved during YAML parsing, before schema validation
473+
- The `vars` section itself is ignored by NetGraph runtime logic
474+
- Anchors can be defined in any section, not just `vars`
475+
- Merge operations follow YAML 1.1 semantics (later keys override earlier ones)
476+
445477
## `workflow` - Execution Steps
446478

447479
A list of operations to perform on the network. Each step has a `step_type` and specific arguments. This section defines the analysis workflow to be executed.

ngraph/scenario.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ def from_yaml(
7979
with a default ComponentsLibrary if provided.
8080
8181
Top-level YAML keys can include:
82+
- vars
8283
- blueprints
8384
- network
8485
- failure_policy_set
@@ -92,6 +93,7 @@ def from_yaml(
9293
If 'failure_policy_set' is omitted, scenario.failure_policy_set is empty.
9394
If 'components' is provided, it is merged with default_components.
9495
If 'seed' is provided, it enables reproducible random operations.
96+
If 'vars' is provided, it can contain YAML anchors and aliases for reuse.
9597
If any unrecognized top-level key is found, a ValueError is raised.
9698
9799
Args:
@@ -115,6 +117,7 @@ def from_yaml(
115117

116118
# Ensure only recognized top-level keys are present.
117119
recognized_keys = {
120+
"vars",
118121
"blueprints",
119122
"network",
120123
"failure_policy_set",

schemas/scenario.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55
"description": "JSON Schema for NetGraph network scenario YAML files",
66
"type": "object",
77
"properties": {
8+
"vars": {
9+
"type": "object",
10+
"description": "YAML anchors and variable definitions for reuse throughout the scenario"
11+
},
812
"seed": {
913
"type": "integer",
1014
"description": "Master seed for reproducible random operations across the scenario"

tests/test_scenario.py

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -645,3 +645,87 @@ def test_failure_policy_docstring_yaml_full_scenario_integration():
645645
# N2, N3 should not fail (different electric grids)
646646
assert "N2" not in failed
647647
assert "N3" not in failed
648+
649+
650+
def test_yaml_anchors_and_aliases():
651+
"""Test that YAML anchors and aliases work correctly with the vars section."""
652+
scenario_yaml = """
653+
vars:
654+
default_capacity: &default_cap 100
655+
common_attrs: &common_attrs
656+
region: "datacenter1"
657+
type: "switch"
658+
659+
network:
660+
name: "anchor_test"
661+
nodes:
662+
N1:
663+
attrs:
664+
<<: *common_attrs
665+
capacity: *default_cap
666+
N2:
667+
attrs:
668+
<<: *common_attrs
669+
capacity: *default_cap
670+
type: "router" # Override the type
671+
links:
672+
- source: N1
673+
target: N2
674+
link_params:
675+
capacity: *default_cap
676+
677+
traffic_matrix_set:
678+
default: []
679+
"""
680+
681+
# Should load without errors
682+
scenario = Scenario.from_yaml(scenario_yaml)
683+
684+
# Verify the anchors were properly expanded
685+
n1_attrs = scenario.network.nodes["N1"].attrs
686+
n2_attrs = scenario.network.nodes["N2"].attrs
687+
688+
# Both nodes should have the common attributes
689+
assert n1_attrs["region"] == "datacenter1"
690+
assert n2_attrs["region"] == "datacenter1"
691+
assert n1_attrs["capacity"] == 100
692+
assert n2_attrs["capacity"] == 100
693+
694+
# N1 should have the original type from the anchor
695+
assert n1_attrs["type"] == "switch"
696+
# N2 should have the overridden type
697+
assert n2_attrs["type"] == "router"
698+
699+
# The link should also use the anchored capacity
700+
link = list(scenario.network.links.values())[0]
701+
assert link.capacity == 100
702+
703+
704+
def test_yaml_anchors_without_vars_section():
705+
"""Test that YAML anchors work even without an explicit vars section."""
706+
scenario_yaml = """
707+
network:
708+
name: "anchor_test"
709+
nodes:
710+
N1: &common_node
711+
attrs:
712+
region: "datacenter1"
713+
type: "switch"
714+
N2:
715+
<<: *common_node
716+
attrs:
717+
region: "datacenter2"
718+
type: "router"
719+
links: []
720+
721+
traffic_matrix_set:
722+
default: []
723+
"""
724+
725+
# Should load without errors
726+
scenario = Scenario.from_yaml(scenario_yaml)
727+
728+
# Verify the network was created correctly
729+
assert len(scenario.network.nodes) == 2
730+
assert "N1" in scenario.network.nodes
731+
assert "N2" in scenario.network.nodes

tests/test_schema_validation.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,19 @@ def test_schema_requires_risk_group_name(self, schema):
124124
with pytest.raises(jsonschema.ValidationError):
125125
jsonschema.validate(invalid_data, schema)
126126

127+
def test_schema_validates_vars_section(self, schema):
128+
"""Test that the schema validates the vars section for YAML anchors."""
129+
valid_data = {
130+
"vars": {
131+
"default_capacity": 100,
132+
"common_attrs": {"region": "datacenter1", "type": "switch"},
133+
},
134+
"network": {"nodes": {}, "links": []},
135+
}
136+
137+
# Should not raise any validation errors
138+
jsonschema.validate(valid_data, schema)
139+
127140
def test_schema_validates_link_risk_groups(self, schema):
128141
"""Test that the schema validates risk_groups in link_params."""
129142
valid_data = {

0 commit comments

Comments
 (0)