diff --git a/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/configs/relaxed-structured-config-validation.cfg b/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/configs/relaxed-structured-config-validation.cfg
new file mode 100644
index 00000000000..85e22f7b913
--- /dev/null
+++ b/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/configs/relaxed-structured-config-validation.cfg
@@ -0,0 +1,25 @@
+!
+no enable password
+no aaa root
+!
+vlan internal order ascending range 1006 1199
+!
+transceiver qsfp default-mode 4x10G
+!
+service routing protocols model multi-agent
+!
+hostname relaxed-structured-config-validation
+!
+vrf instance MGMT
+!
+management api http-commands
+ protocol https
+ no shutdown
+ !
+ vrf MGMT
+ no shutdown
+!
+aaa accounting exec console start-stop group node_group
+no ip routing vrf MGMT
+!
+end
diff --git a/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/relaxed-structured-config-validation.yml b/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/relaxed-structured-config-validation.yml
new file mode 100644
index 00000000000..2e1da556d87
--- /dev/null
+++ b/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/relaxed-structured-config-validation.yml
@@ -0,0 +1,28 @@
+hostname: relaxed-structured-config-validation
+is_deployed: true
+service_routing_protocols_model: multi-agent
+vlan_internal_order:
+ allocation: ascending
+ range:
+ beginning: 1006
+ ending: 1199
+aaa_root:
+ disabled: true
+config_end: true
+enable_password:
+ disabled: true
+transceiver_qsfp_default_mode_4x10: true
+vrfs:
+- name: MGMT
+ ip_routing: false
+management_api_http:
+ enable_vrfs:
+ - name: MGMT
+ enable_https: true
+ip_igmp_snooping:
+ globally_enabled: true
+aaa_accounting:
+ exec:
+ console:
+ group: node_group
+ type: start-stop
diff --git a/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/inventory/host_vars/relaxed-structured-config-validation.yml b/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/inventory/host_vars/relaxed-structured-config-validation.yml
new file mode 100644
index 00000000000..0ed087dadef
--- /dev/null
+++ b/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/inventory/host_vars/relaxed-structured-config-validation.yml
@@ -0,0 +1,19 @@
+---
+# The required key 'type' is added via custom_structured_configuration.
+custom_structured_configuration_aaa_accounting:
+ exec:
+ console:
+ type: start-stop
+
+type: l2leaf
+
+l2leaf:
+ nodes:
+ - name: relaxed-structured-config-validation
+ structured_config:
+ aaa_accounting:
+ exec:
+ console:
+ # Adding group here but not including the required key 'type'.
+ # This will not raise a validation error because of relax mode in the schema.
+ group: node_group
diff --git a/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/inventory/hosts.yml b/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/inventory/hosts.yml
index 62a5b1e8ca0..19c15c67bbb 100644
--- a/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/inventory/hosts.yml
+++ b/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/inventory/hosts.yml
@@ -13,6 +13,7 @@ all:
children:
CLEAN_UNIT_TESTS: # Single Devices testing one specific case and only using hostvars
hosts:
+ relaxed-structured-config-validation:
always-configure-ip-routing:
bgp-options:
connected_endpoints:
diff --git a/ansible_collections/arista/avd/roles/eos_cli_config_gen/docs/tables/aaa-accounting.md b/ansible_collections/arista/avd/roles/eos_cli_config_gen/docs/tables/aaa-accounting.md
index 01a2c528802..a98e3cc9320 100644
--- a/ansible_collections/arista/avd/roles/eos_cli_config_gen/docs/tables/aaa-accounting.md
+++ b/ansible_collections/arista/avd/roles/eos_cli_config_gen/docs/tables/aaa-accounting.md
@@ -10,7 +10,7 @@
| [aaa_accounting](## "aaa_accounting") | Dictionary | | | | |
| [ exec](## "aaa_accounting.exec") | Dictionary | | | | |
| [ console](## "aaa_accounting.exec.console") | Dictionary | | | | |
- | [ type](## "aaa_accounting.exec.console.type") | String | | | Valid Values:
- none
- start-stop
- stop-only
| |
+ | [ type](## "aaa_accounting.exec.console.type") | String | Required | | Valid Values:
- none
- start-stop
- stop-only
| |
| [ group](## "aaa_accounting.exec.console.group") | String | | | | Group Name. |
| [ logging](## "aaa_accounting.exec.console.logging") | Boolean | | | | |
| [ default](## "aaa_accounting.exec.default") | Dictionary | | | | |
@@ -43,7 +43,7 @@
aaa_accounting:
exec:
console:
- type:
+ type:
# Group Name.
group:
diff --git a/python-avd/pyavd/_eos_cli_config_gen/schema/__init__.py b/python-avd/pyavd/_eos_cli_config_gen/schema/__init__.py
index 05f524ba330..dbccb61c640 100644
--- a/python-avd/pyavd/_eos_cli_config_gen/schema/__init__.py
+++ b/python-avd/pyavd/_eos_cli_config_gen/schema/__init__.py
@@ -28,7 +28,7 @@ class Console(AvdModel):
"""Subclass of AvdModel."""
_fields: ClassVar[dict] = {"type": {"type": str}, "group": {"type": str}, "logging": {"type": bool}, "_custom_data": {"type": dict}}
- type: Literal["none", "start-stop", "stop-only"] | None
+ type: Literal["none", "start-stop", "stop-only"]
group: str | None
"""Group Name."""
logging: bool | None
@@ -39,7 +39,7 @@ class Console(AvdModel):
def __init__(
self,
*,
- type: Literal["none", "start-stop", "stop-only"] | None | UndefinedType = Undefined,
+ type: Literal["none", "start-stop", "stop-only"] | UndefinedType = Undefined,
group: str | None | UndefinedType = Undefined,
logging: bool | None | UndefinedType = Undefined,
_custom_data: dict[str, Any] | UndefinedType = Undefined,
diff --git a/python-avd/pyavd/_eos_cli_config_gen/schema/eos_cli_config_gen.schema.yml b/python-avd/pyavd/_eos_cli_config_gen/schema/eos_cli_config_gen.schema.yml
index 092b4cf020e..9fddab8f82e 100644
--- a/python-avd/pyavd/_eos_cli_config_gen/schema/eos_cli_config_gen.schema.yml
+++ b/python-avd/pyavd/_eos_cli_config_gen/schema/eos_cli_config_gen.schema.yml
@@ -24,6 +24,7 @@ keys:
- none
- start-stop
- stop-only
+ required: true
group:
description: Group Name.
type: str
diff --git a/python-avd/pyavd/_eos_cli_config_gen/schema/schema_fragments/aaa_accounting.schema.yml b/python-avd/pyavd/_eos_cli_config_gen/schema/schema_fragments/aaa_accounting.schema.yml
index dcbc097c9a2..437b370effa 100644
--- a/python-avd/pyavd/_eos_cli_config_gen/schema/schema_fragments/aaa_accounting.schema.yml
+++ b/python-avd/pyavd/_eos_cli_config_gen/schema/schema_fragments/aaa_accounting.schema.yml
@@ -18,6 +18,7 @@ keys:
type:
type: str
valid_values: ["none", "start-stop", "stop-only"]
+ required: true
group:
description: Group Name.
type: str
diff --git a/python-avd/pyavd/_eos_cli_config_gen/schema/schema_fragments/access_lists.schema.yml b/python-avd/pyavd/_eos_cli_config_gen/schema/schema_fragments/access_lists.schema.yml
index 8891d3ea3fc..1ac86f3346a 100644
--- a/python-avd/pyavd/_eos_cli_config_gen/schema/schema_fragments/access_lists.schema.yml
+++ b/python-avd/pyavd/_eos_cli_config_gen/schema/schema_fragments/access_lists.schema.yml
@@ -16,7 +16,8 @@ keys:
name:
type: str
description: Access-list Name.
- convert_types: [ int ]
+ convert_types:
+ - int
counters_per_entry:
type: bool
permit_response_traffic:
diff --git a/python-avd/pyavd/_eos_designs/schema/eos_designs.schema.yml b/python-avd/pyavd/_eos_designs/schema/eos_designs.schema.yml
index b241f60331a..9b77a0373a5 100644
--- a/python-avd/pyavd/_eos_designs/schema/eos_designs.schema.yml
+++ b/python-avd/pyavd/_eos_designs/schema/eos_designs.schema.yml
@@ -204,6 +204,7 @@ keys:
default: false
structured_config:
type: dict
+ relaxed_validation: true
description: Custom structured config added under router_bgp.peer_groups.[name=]
for eos_cli_config_gen.
documentation_options:
@@ -224,6 +225,7 @@ keys:
default: false
structured_config:
type: dict
+ relaxed_validation: true
description: Custom structured config added under router_bgp.peer_groups.[name=]
for eos_cli_config_gen.
documentation_options:
@@ -244,6 +246,7 @@ keys:
default: true
structured_config:
type: dict
+ relaxed_validation: true
description: Custom structured config added under router_bgp.peer_groups.[name=]
for eos_cli_config_gen.
documentation_options:
@@ -264,6 +267,7 @@ keys:
default: true
structured_config:
type: dict
+ relaxed_validation: true
description: Custom structured config added under router_bgp.peer_groups.[name=]
for eos_cli_config_gen.
documentation_options:
@@ -284,6 +288,7 @@ keys:
default: true
structured_config:
type: dict
+ relaxed_validation: true
description: Custom structured config added under router_bgp.peer_groups.[name=]
for eos_cli_config_gen.
documentation_options:
@@ -304,6 +309,7 @@ keys:
default: true
structured_config:
type: dict
+ relaxed_validation: true
description: Custom structured config added under router_bgp.peer_groups.[name=]
for eos_cli_config_gen.
documentation_options:
@@ -324,6 +330,7 @@ keys:
default: true
structured_config:
type: dict
+ relaxed_validation: true
description: Custom structured config added under router_bgp.peer_groups.[name=]
for eos_cli_config_gen.
documentation_options:
@@ -389,6 +396,7 @@ keys:
default: 1
structured_config:
type: dict
+ relaxed_validation: true
description: Custom structured config added under router_bgp.peer_groups.[name=]
for eos_cli_config_gen.
documentation_options:
@@ -446,6 +454,7 @@ keys:
default: 1
structured_config:
type: dict
+ relaxed_validation: true
description: Custom structured config added under router_bgp.peer_groups.[name=]
for eos_cli_config_gen.
documentation_options:
@@ -3282,6 +3291,7 @@ keys:
description: Only use entropy from the hardware source.
structured_config:
type: dict
+ relaxed_validation: true
documentation_options:
hide_keys: true
description: Custom structured config for eos_cli_config_gen.
@@ -5655,6 +5665,7 @@ $defs:
the final EOS configuration.
structured_config:
type: dict
+ relaxed_validation: true
description: Custom structured config added under port_channel_interfaces.[name=]
for eos_cli_config_gen.
documentation_options:
@@ -5682,6 +5693,7 @@ $defs:
EOS configuration.
structured_config:
type: dict
+ relaxed_validation: true
description: Custom structured config added under ethernet_interfaces.[name=]
for eos_cli_config_gen.
documentation_options:
@@ -6822,6 +6834,7 @@ $defs:
`fabric_flow_tracking.l3_interfaces` setting.
structured_config:
type: dict
+ relaxed_validation: true
description: Custom structured config added under ethernet_interfaces.[name=]
for eos_cli_config_gen.
documentation_options:
@@ -7154,6 +7167,7 @@ $defs:
'
structured_config:
type: dict
+ relaxed_validation: true
description: Custom structured config added under router_bgp.vrfs.[name=]
for eos_cli_config_gen.
documentation_options:
@@ -7262,6 +7276,7 @@ $defs:
EOS configuration.
structured_config:
type: dict
+ relaxed_validation: true
description: Custom structured config for eos_cli_config_gen.
documentation_options:
hide_keys: true
@@ -7442,6 +7457,7 @@ $defs:
keys:
structured_config:
type: dict
+ relaxed_validation: true
description: 'Custom structured config added under router_bgp.vlans.[id=]
for eos_cli_config_gen.
@@ -7730,6 +7746,7 @@ $defs:
hide_keys: true
description: Custom structured config for eos_cli_config_gen.
type: dict
+ relaxed_validation: true
$ref: eos_cli_config_gen#
uplink_type:
documentation_options:
@@ -7938,6 +7955,7 @@ $defs:
table: node-type-uplink-configuration
hide_keys: true
type: dict
+ relaxed_validation: true
description: 'Custom structured config applied to "uplink_interfaces",
and "uplink_switch_interfaces".
@@ -7962,6 +7980,7 @@ $defs:
table: node-type-l2-mlag-configuration
hide_keys: true
type: dict
+ relaxed_validation: true
description: 'Custom structured config applied to MLAG peer link port-channel
id.
@@ -7979,6 +7998,7 @@ $defs:
table: node-type-l2-mlag-configuration
hide_keys: true
type: dict
+ relaxed_validation: true
description: 'Custom structured config applied to MLAG Peer Link (control
link) SVI interface id.
@@ -7996,6 +8016,7 @@ $defs:
table: node-type-l2-mlag-configuration
hide_keys: true
type: dict
+ relaxed_validation: true
description: 'Custom structured config applied to MLAG underlay L3 peering
SVI interface id.
@@ -9508,6 +9529,7 @@ $defs:
setting.
structured_config:
type: dict
+ relaxed_validation: true
documentation_options:
hide_keys: true
description: Custom structured config for the Ethernet interface.
@@ -9722,6 +9744,7 @@ $defs:
- ebgp: Enforce plain IPv4 BGP peering'
structured_config:
type: dict
+ relaxed_validation: true
documentation_options:
hide_keys: true
description: 'Custom structured config for interfaces.
@@ -10064,6 +10087,7 @@ $defs:
keys:
structured_config:
type: dict
+ relaxed_validation: true
description: 'Structured configuration and EOS CLI commands rendered on
router_bgp.vlans.[id=].
@@ -10087,6 +10111,7 @@ $defs:
'
structured_config:
type: dict
+ relaxed_validation: true
description: 'Custom structured config added under vlan_interfaces.[name=]
for eos_cli_config_gen.
diff --git a/python-avd/pyavd/_eos_designs/schema/schema_fragments/bgp_peer_groups.schema.yml b/python-avd/pyavd/_eos_designs/schema/schema_fragments/bgp_peer_groups.schema.yml
index 0c71f1b866a..0be78d985ed 100644
--- a/python-avd/pyavd/_eos_designs/schema/schema_fragments/bgp_peer_groups.schema.yml
+++ b/python-avd/pyavd/_eos_designs/schema/schema_fragments/bgp_peer_groups.schema.yml
@@ -29,6 +29,7 @@ keys:
default: False
structured_config:
type: dict
+ relaxed_validation: true
description: Custom structured config added under router_bgp.peer_groups.[name=] for eos_cli_config_gen.
documentation_options:
hide_keys: true
@@ -48,6 +49,7 @@ keys:
default: False
structured_config:
type: dict
+ relaxed_validation: true
description: Custom structured config added under router_bgp.peer_groups.[name=] for eos_cli_config_gen.
documentation_options:
hide_keys: true
@@ -67,6 +69,7 @@ keys:
default: True
structured_config:
type: dict
+ relaxed_validation: true
description: Custom structured config added under router_bgp.peer_groups.[name=] for eos_cli_config_gen.
documentation_options:
hide_keys: true
@@ -86,6 +89,7 @@ keys:
default: True
structured_config:
type: dict
+ relaxed_validation: true
description: Custom structured config added under router_bgp.peer_groups.[name=] for eos_cli_config_gen.
documentation_options:
hide_keys: true
@@ -105,6 +109,7 @@ keys:
default: True
structured_config:
type: dict
+ relaxed_validation: true
description: Custom structured config added under router_bgp.peer_groups.[name=] for eos_cli_config_gen.
documentation_options:
hide_keys: true
@@ -124,6 +129,7 @@ keys:
default: True
structured_config:
type: dict
+ relaxed_validation: true
description: Custom structured config added under router_bgp.peer_groups.[name=] for eos_cli_config_gen.
documentation_options:
hide_keys: true
@@ -143,6 +149,7 @@ keys:
default: True
structured_config:
type: dict
+ relaxed_validation: true
description: Custom structured config added under router_bgp.peer_groups.[name=] for eos_cli_config_gen.
documentation_options:
hide_keys: true
@@ -197,6 +204,7 @@ keys:
default: 1
structured_config:
type: dict
+ relaxed_validation: true
description: Custom structured config added under router_bgp.peer_groups.[name=] for eos_cli_config_gen.
documentation_options:
hide_keys: true
@@ -244,6 +252,7 @@ keys:
default: 1
structured_config:
type: dict
+ relaxed_validation: true
description: Custom structured config added under router_bgp.peer_groups.[name=] for eos_cli_config_gen.
documentation_options:
hide_keys: true
diff --git a/python-avd/pyavd/_eos_designs/schema/schema_fragments/defs_adapter_config.schema.yml b/python-avd/pyavd/_eos_designs/schema/schema_fragments/defs_adapter_config.schema.yml
index cfc5883f5bf..42733f30bd8 100644
--- a/python-avd/pyavd/_eos_designs/schema/schema_fragments/defs_adapter_config.schema.yml
+++ b/python-avd/pyavd/_eos_designs/schema/schema_fragments/defs_adapter_config.schema.yml
@@ -483,6 +483,7 @@ $defs:
description: EOS CLI rendered directly on the port-channel interface in the final EOS configuration.
structured_config:
type: dict
+ relaxed_validation: true
description: Custom structured config added under port_channel_interfaces.[name=] for eos_cli_config_gen.
documentation_options:
hide_keys: true
@@ -506,6 +507,7 @@ $defs:
description: EOS CLI rendered directly on the ethernet interface in the final EOS configuration.
structured_config:
type: dict
+ relaxed_validation: true
description: Custom structured config added under ethernet_interfaces.[name=] for eos_cli_config_gen.
documentation_options:
hide_keys: true
diff --git a/python-avd/pyavd/_eos_designs/schema/schema_fragments/defs_network_services.schema.yml b/python-avd/pyavd/_eos_designs/schema/schema_fragments/defs_network_services.schema.yml
index 8bf38f18c9a..c09e726b3a0 100644
--- a/python-avd/pyavd/_eos_designs/schema/schema_fragments/defs_network_services.schema.yml
+++ b/python-avd/pyavd/_eos_designs/schema/schema_fragments/defs_network_services.schema.yml
@@ -787,6 +787,7 @@ $defs:
Configures flow-tracking on the interface. Overrides `fabric_flow_tracking.l3_interfaces` setting.
structured_config:
type: dict
+ relaxed_validation: true
description: Custom structured config added under ethernet_interfaces.[name=] for eos_cli_config_gen.
documentation_options:
hide_keys: true
@@ -1064,6 +1065,7 @@ $defs:
EOS CLI rendered directly on the Router BGP, VRF definition in the final EOS configuration.
structured_config:
type: dict
+ relaxed_validation: true
description: Custom structured config added under router_bgp.vrfs.[name=] for eos_cli_config_gen.
documentation_options:
hide_keys: true
@@ -1158,6 +1160,7 @@ $defs:
description: EOS CLI rendered directly on the root level of the final EOS configuration.
structured_config:
type: dict
+ relaxed_validation: true
description: Custom structured config for eos_cli_config_gen.
documentation_options:
hide_keys: true
@@ -1304,6 +1307,7 @@ $defs:
keys:
structured_config:
type: dict
+ relaxed_validation: true
description: |
Custom structured config added under router_bgp.vlans.[id=] for eos_cli_config_gen.
This configuration will not be applied to vlan aware bundles.
diff --git a/python-avd/pyavd/_eos_designs/schema/schema_fragments/defs_node_type.schema.yml b/python-avd/pyavd/_eos_designs/schema/schema_fragments/defs_node_type.schema.yml
index af03a895337..c887e4abf79 100644
--- a/python-avd/pyavd/_eos_designs/schema/schema_fragments/defs_node_type.schema.yml
+++ b/python-avd/pyavd/_eos_designs/schema/schema_fragments/defs_node_type.schema.yml
@@ -164,6 +164,7 @@ $defs:
hide_keys: true
description: Custom structured config for eos_cli_config_gen.
type: dict
+ relaxed_validation: true
$ref: "eos_cli_config_gen#"
uplink_type:
documentation_options:
@@ -340,6 +341,7 @@ $defs:
hide_keys: true
# TODO: Add deprecation warning when introducing new keys for specific configuration.
type: dict
+ relaxed_validation: true
description: |
Custom structured config applied to "uplink_interfaces", and "uplink_switch_interfaces".
When uplink_type == "p2p", custom structured config added under ethernet_interfaces.[name=] for eos_cli_config_gen overrides the settings on the ethernet interface level.
@@ -354,6 +356,7 @@ $defs:
table: node-type-l2-mlag-configuration
hide_keys: true
type: dict
+ relaxed_validation: true
description: |
Custom structured config applied to MLAG peer link port-channel id.
Added under port_channel_interfaces.[name=] for eos_cli_config_gen.
@@ -365,6 +368,7 @@ $defs:
table: node-type-l2-mlag-configuration
hide_keys: true
type: dict
+ relaxed_validation: true
description: |
Custom structured config applied to MLAG Peer Link (control link) SVI interface id.
Added under vlan_interfaces.[name=] for eos_cli_config_gen.
@@ -376,6 +380,7 @@ $defs:
table: node-type-l2-mlag-configuration
hide_keys: true
type: dict
+ relaxed_validation: true
description: |
Custom structured config applied to MLAG underlay L3 peering SVI interface id.
Added under vlan_interfaces.[name=] for eos_cli_config_gen.
diff --git a/python-avd/pyavd/_eos_designs/schema/schema_fragments/defs_node_type_l3_interfaces.schema.yml b/python-avd/pyavd/_eos_designs/schema/schema_fragments/defs_node_type_l3_interfaces.schema.yml
index 68a3e99a8b0..c8ec13cb031 100644
--- a/python-avd/pyavd/_eos_designs/schema/schema_fragments/defs_node_type_l3_interfaces.schema.yml
+++ b/python-avd/pyavd/_eos_designs/schema/schema_fragments/defs_node_type_l3_interfaces.schema.yml
@@ -179,6 +179,7 @@ $defs:
Configures flow-tracking on the interface. Overrides `fabric_flow_tracking.l3_interfaces` setting.
structured_config:
type: dict
+ relaxed_validation: true
documentation_options:
hide_keys: true
description: |-
diff --git a/python-avd/pyavd/_eos_designs/schema/schema_fragments/defs_p2p_links.schema.yml b/python-avd/pyavd/_eos_designs/schema/schema_fragments/defs_p2p_links.schema.yml
index 88e455074c7..62fc8a436ef 100644
--- a/python-avd/pyavd/_eos_designs/schema/schema_fragments/defs_p2p_links.schema.yml
+++ b/python-avd/pyavd/_eos_designs/schema/schema_fragments/defs_p2p_links.schema.yml
@@ -200,6 +200,7 @@ $defs:
- ebgp: Enforce plain IPv4 BGP peering
structured_config:
type: dict
+ relaxed_validation: true
documentation_options:
hide_keys: true
description: |-
diff --git a/python-avd/pyavd/_eos_designs/schema/schema_fragments/defs_svi_settings.schema.yml b/python-avd/pyavd/_eos_designs/schema/schema_fragments/defs_svi_settings.schema.yml
index 579a1b2f1a1..99439dd61ad 100644
--- a/python-avd/pyavd/_eos_designs/schema/schema_fragments/defs_svi_settings.schema.yml
+++ b/python-avd/pyavd/_eos_designs/schema/schema_fragments/defs_svi_settings.schema.yml
@@ -272,6 +272,7 @@ $defs:
keys:
structured_config:
type: dict
+ relaxed_validation: true
description: |
Structured configuration and EOS CLI commands rendered on router_bgp.vlans.[id=].
This configuration will not be applied to vlan aware bundles.
@@ -288,6 +289,7 @@ $defs:
EOS CLI rendered directly on the VLAN interface in the final EOS configuration.
structured_config:
type: dict
+ relaxed_validation: true
description: |
Custom structured config added under vlan_interfaces.[name=] for eos_cli_config_gen.
documentation_options:
diff --git a/python-avd/pyavd/_eos_designs/schema/schema_fragments/platform_settings.schema.yml b/python-avd/pyavd/_eos_designs/schema/schema_fragments/platform_settings.schema.yml
index c493a8a90b7..d5f454146d0 100644
--- a/python-avd/pyavd/_eos_designs/schema/schema_fragments/platform_settings.schema.yml
+++ b/python-avd/pyavd/_eos_designs/schema/schema_fragments/platform_settings.schema.yml
@@ -134,6 +134,7 @@ keys:
description: Only use entropy from the hardware source.
structured_config:
type: dict
+ relaxed_validation: true
documentation_options:
hide_keys: true
description: Custom structured config for eos_cli_config_gen.
diff --git a/python-avd/pyavd/_schema/avd_meta_schema.json b/python-avd/pyavd/_schema/avd_meta_schema.json
index bc97a16186d..0a19301094b 100644
--- a/python-avd/pyavd/_schema/avd_meta_schema.json
+++ b/python-avd/pyavd/_schema/avd_meta_schema.json
@@ -337,6 +337,11 @@
"required": {
"$ref": "#/$defs/required"
},
+ "relaxed_validation": {
+ "type": "boolean",
+ "default": false,
+ "description": "Disable required key validation."
+ },
"deprecation": {
"$ref": "#/$defs/deprecation"
},
@@ -460,4 +465,4 @@
},
"title": "Arista AVD Schema",
"$ref": "#/$defs/avd_schema_var"
-}
\ No newline at end of file
+}
diff --git a/python-avd/pyavd/_schema/avdvalidator.py b/python-avd/pyavd/_schema/avdvalidator.py
index 5845cc2f38b..558cdd3a529 100644
--- a/python-avd/pyavd/_schema/avdvalidator.py
+++ b/python-avd/pyavd/_schema/avdvalidator.py
@@ -31,6 +31,8 @@ def __init__(self, schema: dict) -> None:
"unique_keys": self.unique_keys_validator,
"$ref": self.ref_validator,
}
+ self._relaxed_validation: bool = False
+ """Used to ignore missing required keys."""
def validate(self, instance: Any, schema: dict | None = None, path: list[str | int] | None = None) -> Generator:
if schema is None:
@@ -50,6 +52,7 @@ def validate(self, instance: Any, schema: dict | None = None, path: list[str | i
yield from validator(schema_value, instance, schema, path)
def type_validator(self, schema_type: str, instance: Any, _schema: dict, path: list[str | int]) -> Generator:
+ """Validates the type of `instance` equal to `schema_type`."""
if not self.is_type(instance, schema_type):
yield AvdValidationError(
f"Invalid type '{type(instance).__name__}'. Expected a '{schema_type}'.",
@@ -57,6 +60,7 @@ def type_validator(self, schema_type: str, instance: Any, _schema: dict, path: l
)
def unique_keys_validator(self, unique_keys: list[str], instance: list, _schema: dict, path: list[str | int]) -> Generator:
+ """Validates that the keys defined in the `unique_keys` list are unique in the `instance`."""
if not instance:
return
@@ -116,16 +120,21 @@ def keys_validator(self, keys: dict, instance: dict, schema: dict, path: list[st
# Validation of "allow_other_keys"
if not schema.get("allow_other_keys", False):
# Check that instance only contains the schema keys
+
invalid_keys = ", ".join([key for key in instance if key not in all_keys and not key.startswith("_")])
if invalid_keys:
yield AvdValidationError(f"Unexpected key(s) '{invalid_keys}' found in dict.", path=path)
+ old_relaxed_validation = self._relaxed_validation
+ if (relaxed_validation := schema.get("relaxed_validation")) is not None:
+ self._relaxed_validation = relaxed_validation
+
# Run over child keys and check for required and update child schema with dynamic valid values before
# descending into validation of child schema.
for key, childschema in all_keys.items():
if instance.get(key) is None:
# Validation of "required" on child keys
- if childschema.get("required"):
+ if childschema.get("required") and not self._relaxed_validation:
yield AvdValidationError(f"Required key '{key}' is not set in dict.", path=path)
# Skip further validation since there is nothing to validate.
@@ -147,6 +156,9 @@ def keys_validator(self, keys: dict, instance: dict, schema: dict, path: list[st
# Perform validation of the modified childschema.
yield from self.validate(instance[key], modified_childschema, path=[*path, key])
+ # Restore value
+ self._relaxed_validation = old_relaxed_validation
+
def dynamic_keys_validator(self, _dynamic_keys: dict, instance: dict, schema: dict, path: list[str | int]) -> Generator:
"""This function triggers the regular "keys" validator in case only dynamic_keys is set."""
if "keys" not in schema:
diff --git a/python-avd/schema_tools/metaschema/meta_schema_model.py b/python-avd/schema_tools/metaschema/meta_schema_model.py
index 70b7d78a5b0..de347e62bbe 100644
--- a/python-avd/schema_tools/metaschema/meta_schema_model.py
+++ b/python-avd/schema_tools/metaschema/meta_schema_model.py
@@ -1,8 +1,27 @@
# Copyright (c) 2023-2024 Arista Networks, Inc.
# Use of this source code is governed by the Apache License 2.0
# that can be found in the LICENSE file.
+"""
+This module provides Pydantic models (classes) representing the meta-schema of the AVD Schema.
+
+Each variable in the schema is called a field, and for each type of field we have a corresponding Pydantic model:
+- AvdSchemaInt
+- AvdSchemaBool
+- AvdSchemaStr
+- AvdSchemaList
+- AvdSchemaDict
+
+The alias "AvdSchemaField" is a union of all the models above, and can be used as an easy type hint for any field type.
+
+All the type-specific Pydantic models inherit the common base class "AvdSchemaBaseModel", and have local overrides
+as needed. For example, only "AvdSchemaList" and "AvdSchemaDict" need to parse child fields.
+
+The overall schema is covered by the class "AristaAvdSchema" which inherits from "AvdSchemaDict" since the root of the schema is a dict.
+"""
+
from __future__ import annotations
+import logging
from abc import ABC
from enum import Enum
from functools import cached_property
@@ -21,24 +40,7 @@
from schema_tools.generate_classes.src_generators import SrcData
-"""
-This module provides Pydantic models (classes) representing the meta-schema of the AVD Schema.
-
-Each variable in the schema is called a field, and for each type of field we have a corresponding Pydantic model:
-- AvdSchemaInt
-- AvdSchemaBool
-- AvdSchemaStr
-- AvdSchemaList
-- AvdSchemaDict
-
-The alias "AvdSchemaField" is a union of all the models above, and can be used as an easy type hint for any field type.
-
-All the type-specific Pydantic models inherit the common base class "AvdSchemaBaseModel", and have local overrides
-as needed. For example, only "AvdSchemaList" and "AvdSchemaDict" need to parse child fields.
-
-The overall schema is covered by the class "AristaAvdSchema" which inherits from "AvdSchemaDict" since the root of the schema is a dict.
-"""
-
+LOGGER = logging.getLogger(__name__)
KEY_PATTERN = r"^[a-z][a-z0-9_]*$"
"""Common pattern to match legal key strings"""
@@ -514,6 +516,8 @@ class DocumentationOptions(AvdSchemaBaseModel.DocumentationOptions):
`schema` is the schema for each key. This is a recursive schema, so the value must conform to AVD Schema.
Note that this is building the schema from values in the _data_ being validated!
"""
+ relaxed_validation: bool | None = False
+ """Disable validation of `required` keys for any children."""
allow_other_keys: bool | None = False
"""Allow keys in the dictionary which are not defined in the schema."""
documentation_options: DocumentationOptions | None = None
diff --git a/python-avd/schema_tools/metaschema/resolvemodel.py b/python-avd/schema_tools/metaschema/resolvemodel.py
index 9b4cd5455cc..7764f45c5bb 100644
--- a/python-avd/schema_tools/metaschema/resolvemodel.py
+++ b/python-avd/schema_tools/metaschema/resolvemodel.py
@@ -38,7 +38,7 @@ def merge_schema_from_ref(schema: dict, resolve_schema: Literal["eos_designs", "
raise ValueError(msg)
pure_ref_schema = (
- {"type", "$ref", "description", "documentation_options", "deprecation"}.issuperset(schema.keys())
+ {"type", "$ref", "description", "documentation_options", "deprecation", "relaxed_validation"}.issuperset(schema.keys())
and resolve_schema not in [None, "all"]
and not schema["$ref"].startswith(f"{resolve_schema}#")
)