Usage
Writing Rules
Section titled “Writing Rules”Semantic validation rules are implemented as Python classes and placed in .py files located in the --rules path.
Structured Rules (Recommended)
Section titled “Structured Rules (Recommended)”Rules can subclass RuleBase and return Violation objects, which enables richer terminal output (title, explanation, recommendation, references) in addition to the violation message.
from nac_validate import RuleBase, Violation
class Rule(RuleBase): id = "101" description = "Verify child naming restrictions" severity = "HIGH" title = "Forbidden child name" explanation = "Child objects named 'FORBIDDEN' are not allowed." recommendation = "Rename the child object to a compliant name." references = ["https://example.com/naming-guidelines"]
@classmethod def match(cls, data): violations = [] try: for child in data["root"]["children"]: if child["name"] == "FORBIDDEN": violations.append( Violation( message=f"root.children.name - {child['name']}", path="root.children", ) ) except KeyError: pass return violationsRuleBase requires id and description class attributes. severity (default HIGH), title, explanation, recommendation, affected_items_label (default Affected Items) and references are all optional. The match classmethod receives the merged YAML data and can optionally accept the Yamale schema as a second argument.
Violation is a dataclass with a required message field, and optional path and details fields.
Simple Rules (String List)
Section titled “Simple Rules (String List)”The original, simpler style is still supported: a plain Rule class whose match classmethod returns a list of violation strings instead of Violation objects.
class Rule: id = "101" description = "Verify child naming restrictions" severity = "HIGH"
@classmethod def match(cls, data): results = [] try: for child in data["root"]["children"]: if child["name"] == "FORBIDDEN": results.append("root.children.name" + " - " + str(child["name"])) except KeyError: pass return resultsExit Codes
Section titled “Exit Codes”nac-validate uses distinct exit codes so CI pipelines can distinguish between failure types:
| Code | Meaning |
|---|---|
| 0 | Validation passed |
| 1 | Semantic validation failed (rule violations) |
| 2 | Syntax validation failed (YAML syntax or schema errors) |
| 3 | Configuration error (e.g. missing schema, invalid rules) |
JSON Output
Section titled “JSON Output”The -f/--format option controls the output format (text or json). JSON output is useful for CI/CD integration:
nac-validate data/ -s schema.yaml -r rules/ --format json{ "syntax_errors": [], "semantic_errors": [ { "rule_id": "301", "description": "Verify Infra VLAN Is Defined When Referenced by AAEPs", "errors": [ "apic.access_policies.aaeps[name=INFRA-AAEP].infra_vlan - AAEP 'INFRA-AAEP' has infra_vlan enabled but global infra_vlan is not defined" ] } ]}Pre-Commit Hook
Section titled “Pre-Commit Hook”The tool can be integrated via a pre-commit hook with the following config (.pre-commit-config.yaml), assuming the default values (.schema.yaml, .rules/) are appropriate:
repos: - repo: https://github.com/netascode/nac-validate rev: v2.0.0 hooks: - id: nac-validateIn case the schema or validation rules are located somewhere else the required CLI arguments can be added like this:
repos: - repo: https://github.com/netascode/nac-validate rev: v2.0.0 hooks: - id: nac-validate args: - '-s' - 'my_schema.yaml' - '-r' - 'rules/'Ansible Vault Support
Section titled “Ansible Vault Support”Values can be encrypted using Ansible Vault. This requires Ansible (ansible-vault command) to be installed and the following two environment variables to be defined:
export ANSIBLE_VAULT_ID=devexport ANSIBLE_VAULT_PASSWORD=Password123ANSIBLE_VAULT_ID is optional, and if not defined will be omitted.
Additional Tags
Section titled “Additional Tags”Reading Environment Variables
Section titled “Reading Environment Variables”The !env YAML tag can be used to read values from environment variables.
root: name: !env VAR_NAME