Skip to main content

A CLI tool to perform syntactic and semantic validation of YAML files.

Project description

Tests Python Support

nac-validate

A CLI tool to perform syntactic and semantic validation of YAML files.

$ nac-validate --help

Usage: nac-validate [OPTIONS] [PATHS]...

A CLI tool to perform syntactic and semantic validation of YAML files.

Arguments:
  [PATHS]...            List of paths pointing to YAML files or directories

Options:
  -v, --verbosity [DEBUG|INFO|WARNING|ERROR|CRITICAL]
                        Verbosity level [env: NAC_VALIDATE_VERBOSITY] [default: WARNING]
  -s, --schema FILE     Path to schema file [env: NAC_VALIDATE_SCHEMA] [default: .schema.yaml]
  -r, --rules DIRECTORY Path(s) to directories with semantic validation rules (repeatable)
                        [env: NAC_VALIDATE_RULES]
  -o, --output FILE     Write merged content from YAML files to a new YAML file
                        [env: NAC_VALIDATE_OUTPUT]
  --non-strict          Accept unexpected elements in YAML files
                        [env: NAC_VALIDATE_NON_STRICT]
  -f, --format [text|json]
                        Output format for validation results
                        [env: NAC_VALIDATE_FORMAT] [default: text]
  --no-color            Disable colored output [env: NO_COLOR]
  --compact             Use compact output format without rule context details
                        [env: NAC_VALIDATE_COMPACT]
  --version             Display version number
  --list-rules          List all available validation rules and exit
  --help                Show this message and exit

Exit Codes

The CLI uses specific exit codes to help automation distinguish between error types:

Exit Code Meaning
0 Validation passed
1 Semantic validation failed (business rule violations)
2 Syntax validation failed (YAML syntax or schema errors)
3 Configuration error (missing schema, invalid rules, etc.)

How It Works

Syntactic validation is done by basic YAML syntax validation (e.g., indentation) and by providing a Yamale schema and validating all YAML files against that schema. Semantic validation is done by providing a set of rules (implemented in Python) which are then validated against the YAML data. Every rule is implemented as a Python class and should be placed in a .py file located in the --rules path.

Writing Validation Rules

Each .py file must have a single class named Rule that subclasses RuleBase. This class must set id and description as class attributes. It must implement a classmethod() named match that has a single function argument data which is the data read from all YAML files. It can optionally also have a second argument schema which would then provide the Yamale schema.

Simple Rules (String List)

For simple validations, rules can return a list of strings describing each violation:

from nac_validate import RuleBase


class Rule(RuleBase):
    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 results

Structured Rules (Recommended)

For richer output with context, explanations, and recommendations, subclass RuleBase and set the rich context attributes as class-level fields:

from nac_validate import RuleBase, Violation


class Rule(RuleBase):
    id = "301"
    description = "Verify Infra VLAN Is Defined When Referenced by AAEPs"
    severity = "HIGH"

    # Rich context displayed in terminal output
    title = "INFRA VLAN CONFIGURATION WARNING"
    affected_items_label = "Affected AAEPs"
    explanation = """\
The Infrastructure VLAN (Infra VLAN) is critical for APIC-to-leaf
communication. When infra_vlan is enabled on an AAEP, the global
infra_vlan value must be explicitly defined."""
    recommendation = """\
Define the Infra VLAN in your access_policies configuration:

  apic:
    access_policies:
      infra_vlan: 3967
      aaeps:
        - name: INFRA-AAEP
          infra_vlan: true"""
    references = [
        "https://www.cisco.com/c/en/us/td/docs/dcn/aci/apic/all/apic-fabric-access-policies.html"
    ]

    @classmethod
    def match(cls, inventory):
        violations = []

        aaeps = inventory.get("apic", {}).get("access_policies", {}).get("aaeps", [])
        if aaeps is None:
            aaeps = []

        # Find AAEPs with infra_vlan enabled
        affected_aaeps = [
            aaep.get("name", "unnamed")
            for aaep in aaeps
            if aaep.get("infra_vlan", False)
        ]

        if affected_aaeps:
            infra_vlan = (
                inventory.get("apic", {})
                .get("access_policies", {})
                .get("infra_vlan", 0)
            )
            if infra_vlan == 0:
                for aaep_name in affected_aaeps:
                    violations.append(
                        Violation(
                            message=f"AAEP '{aaep_name}' has infra_vlan enabled but global infra_vlan is not defined",
                            path=f"apic.access_policies.aaeps[name={aaep_name}].infra_vlan",
                            details={
                                "aaep_name": aaep_name,
                                "infra_vlan_enabled": True,
                                "global_infra_vlan_defined": False,
                            },
                        )
                    )

        return violations

RuleBase Attributes

All rules must subclass RuleBase and set at minimum id and description. The following class attributes are available:

Attribute Required Default Description
id Yes Unique rule identifier
description Yes Short description of the rule
severity No "HIGH" Severity level (HIGH, MEDIUM, LOW)
title No "" Header displayed in violation output
explanation No "" Detailed explanation of why this matters
recommendation No "" How to fix the issue, with examples
affected_items_label No "Affected Items" Label for the violations list
references No [] Links to documentation

Rich terminal output sections (title header, explanation, recommendation, references) are rendered individually for whichever attributes are set. Setting any of title, explanation, or recommendation enables enhanced output; only populated sections are shown.

Violation - Represents a single validation failure:

  • message (str): Human-readable description of the issue
  • path (str): Location in the YAML structure (e.g., apic.tenants[name=PROD].vrfs[0])
  • details (dict, optional): Machine-readable metadata for automation

JSON Output

Use --format json for machine-readable output suitable for CI/CD pipelines:

nac-validate data/ -s schema.yaml -r rules/ --format json

Output structure:

{
  "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"
      ]
    }
  ]
}

Installation

Python 3.10+ is required to install nac-validate. Don't have Python 3.10 or later? See Python 3 Installation & Setup Guide.

nac-validate can be installed in a virtual environment using pip or uv:

# Using pip
pip install nac-validate

# Using uv (recommended)
uv tools install nac-validate

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: v1.0.0
    hooks:
      - id: nac-validate

In 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: v1.0.0
    hooks:
      - id: nac-validate
        args:
          - '-s'
          - 'my_schema.yaml'
          - '-r'
          - 'rules/'

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=dev
export ANSIBLE_VAULT_PASSWORD=Password123

ANSIBLE_VAULT_ID is optional, and if not defined will be omitted.

Additional Tags

Reading Environment Variables

The !env YAML tag can be used to read values from environment variables.

root:
  name: !env VAR_NAME

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

nac_validate-2.0.0b3.tar.gz (104.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

nac_validate-2.0.0b3-py3-none-any.whl (25.9 kB view details)

Uploaded Python 3

File details

Details for the file nac_validate-2.0.0b3.tar.gz.

File metadata

  • Download URL: nac_validate-2.0.0b3.tar.gz
  • Upload date:
  • Size: 104.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.16 {"installer":{"name":"uv","version":"0.11.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for nac_validate-2.0.0b3.tar.gz
Algorithm Hash digest
SHA256 3d7276d4c3f7c210d5760a85ffe9599c252f014058dd0a68928447231c4b1a2e
MD5 177c9dc6cf4724527da5bcb7191e69ec
BLAKE2b-256 8fe019f6c32e2085764d81e03ff31118d3a05c5dcc63ca43a1862d6dccc4c728

See more details on using hashes here.

File details

Details for the file nac_validate-2.0.0b3-py3-none-any.whl.

File metadata

  • Download URL: nac_validate-2.0.0b3-py3-none-any.whl
  • Upload date:
  • Size: 25.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.16 {"installer":{"name":"uv","version":"0.11.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for nac_validate-2.0.0b3-py3-none-any.whl
Algorithm Hash digest
SHA256 ec3a530e1b25b22eae0cc2aa38586bf13252b7c3c496da318c2da4420cbb989c
MD5 c393be699fa5bd55f850c44ca8dcc430
BLAKE2b-256 fdb2782dd1e9c01d82b1db6044e0ff1239f1eda260985aad5a442a13f5831bac

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page