Skip to content

Generator

Generator Module

generator

Configuration generation functionality.

This module provides functionality to generate device configurations, documentation, and test files from AVD inventory data.

ConfigurationGenerator

ConfigurationGenerator(workflow: str = 'eos-design')

Generator for device configurations.

This class handles generation of device configurations from AVD inventory data using py-avd library.

Examples:

>>> generator = ConfigurationGenerator()
>>> configs = generator.generate(inventory, output_path)
>>> print(f"Generated {len(configs)} configurations")

Parameters:

Name Type Description Default
workflow str

Workflow type (‘eos-design’ or ‘cli-config’), by default “eos-design”

'eos-design'
Source code in avd_cli/logics/generator.py
def __init__(self, workflow: str = "eos-design") -> None:
    """Initialize the configuration generator.

    Parameters
    ----------
    workflow : str, optional
        Workflow type ('eos-design' or 'cli-config'), by default "eos-design"
    """
    self.workflow = normalize_workflow(workflow)
    self.logger = logging.getLogger(f"{__name__}.{self.__class__.__name__}")
    self.pyavd: Any = None  # Will be initialized when needed

generate

generate(
    inventory: InventoryData,
    output_path: Path,
    device_filter: Optional[DeviceFilter] = None,
) -> List[Path]

Generate device configurations.

Parameters:

Name Type Description Default
inventory InventoryData

Loaded inventory data (should contain ALL devices for proper AVD context)

required
output_path Path

Output directory for generated configs

required
device_filter Optional[DeviceFilter]

Filter to determine which devices to generate configs for, by default None. Note: All devices are used for avd_facts calculation, filter only affects which config files are written.

None

Returns:

Type Description
List[Path]

List of generated configuration file paths

Raises:

Type Description
ConfigurationGenerationError

If generation fails

Source code in avd_cli/logics/generator.py
def generate(
    self, inventory: InventoryData, output_path: Path, device_filter: Optional["DeviceFilter"] = None
) -> List[Path]:
    """Generate device configurations.

    Parameters
    ----------
    inventory : InventoryData
        Loaded inventory data (should contain ALL devices for proper AVD context)
    output_path : Path
        Output directory for generated configs
    device_filter : Optional[DeviceFilter], optional
        Filter to determine which devices to generate configs for, by default None.
        Note: All devices are used for avd_facts calculation, filter only affects
        which config files are written.

    Returns
    -------
    List[Path]
        List of generated configuration file paths

    Raises
    ------
    ConfigurationGenerationError
        If generation fails
    """
    self.logger.info("Generating configurations with workflow: %s", self.workflow)

    configs_dir = self._setup_generation(output_path)

    try:
        # Get filtered device list (for determining which files to write)
        filtered_devices = self._filter_devices(inventory, device_filter)
        filtered_hostnames = [d.hostname for d in filtered_devices] if device_filter else None

        # Build pyavd inputs from ALL devices in inventory (for proper AVD context)
        # This ensures avd_facts calculation has complete topology information
        self.logger.info("Building pyavd inputs from resolved inventory (all devices for context)")
        all_devices = inventory.get_all_devices()
        all_inputs = self._build_pyavd_inputs_from_inventory(inventory, all_devices)

        if not all_inputs:
            self.logger.warning("No devices to process")
            return []

        # Generate structured configs for ALL devices (needed for dependencies)
        structured_configs = self._generate_structured_configs(all_inputs)

        # Write config files ONLY for filtered devices
        generated_files = self._write_config_files(structured_configs, configs_dir, filtered_hostnames)

        self.logger.info("Generated %d configuration files", len(generated_files))
        return generated_files

    except ConfigurationGenerationError:
        raise
    except Exception as e:
        raise ConfigurationGenerationError(f"Failed to generate configurations: {e}") from e

DocumentationGenerator

DocumentationGenerator()

Generator for device documentation.

This class handles generation of device documentation from AVD inventory data using py-avd library.

Source code in avd_cli/logics/generator.py
def __init__(self) -> None:
    """Initialize the documentation generator."""
    self.logger = logging.getLogger(f"{__name__}.{self.__class__.__name__}")
    self.pyavd: Any = None

generate

generate(
    inventory: InventoryData,
    output_path: Path,
    device_filter: Optional[DeviceFilter] = None,
) -> List[Path]

Generate device documentation.

Parameters:

Name Type Description Default
inventory InventoryData

Loaded inventory data (should contain ALL devices for proper AVD context)

required
output_path Path

Output directory for generated docs

required
device_filter Optional[DeviceFilter]

Filter to determine which devices to generate docs for, by default None. Note: All devices are used for avd_facts calculation, filter only affects which doc files are written.

None

Returns:

Type Description
List[Path]

List of generated documentation file paths

Raises:

Type Description
DocumentationGenerationError

If generation fails

Source code in avd_cli/logics/generator.py
def generate(
    self, inventory: InventoryData, output_path: Path, device_filter: Optional["DeviceFilter"] = None
) -> List[Path]:
    """Generate device documentation.

    Parameters
    ----------
    inventory : InventoryData
        Loaded inventory data (should contain ALL devices for proper AVD context)
    output_path : Path
        Output directory for generated docs
    device_filter : Optional[DeviceFilter], optional
        Filter to determine which devices to generate docs for, by default None.
        Note: All devices are used for avd_facts calculation, filter only affects
        which doc files are written.

    Returns
    -------
    List[Path]
        List of generated documentation file paths

    Raises
    ------
    DocumentationGenerationError
        If generation fails
    """
    self.logger.info("Generating documentation")

    # Create output directory
    docs_dir = output_path / DEFAULT_DOCS_DIR
    docs_dir.mkdir(parents=True, exist_ok=True)

    generated_files: List[Path] = []

    try:
        # Import pyavd
        pyavd = self._import_pyavd()

        # Reuse the conversion logic from ConfigurationGenerator
        from avd_cli.logics.generator import ConfigurationGenerator

        config_gen = ConfigurationGenerator(workflow="eos-design")

        # Determine which devices to generate docs for
        if device_filter:
            filtered_devices = [
                d for d in inventory.get_all_devices()
                if device_filter.matches_device(d.hostname, d.groups + [d.fabric])
            ]
            filtered_hostnames = {d.hostname for d in filtered_devices}
        else:
            filtered_hostnames = None

        # Build inputs from ALL devices (for proper AVD context)
        all_devices = inventory.get_all_devices()
        all_inputs = config_gen._build_pyavd_inputs_from_inventory(inventory, all_devices)

        if not all_inputs:
            self.logger.warning("No devices to process")
            return generated_files

        # Generate AVD facts and structured configs for ALL devices
        self.logger.info("Generating AVD facts for documentation")
        avd_facts = pyavd.get_avd_facts(all_inputs)

        structured_configs: Dict[str, Dict[str, Any]] = {}
        for hostname, inputs in all_inputs.items():
            structured_config = pyavd.get_device_structured_config(
                hostname=hostname, inputs=inputs, avd_facts=avd_facts
            )
            structured_configs[hostname] = structured_config

        # Generate device documentation ONLY for filtered devices
        hostnames_to_document = (
            filtered_hostnames if filtered_hostnames is not None
            else set(structured_configs.keys())
        )

        self.logger.info("Generating device documentation for %d devices", len(hostnames_to_document))
        for hostname in hostnames_to_document:
            if hostname not in structured_configs:
                continue

            structured_config = structured_configs[hostname]
            doc_file = docs_dir / f"{hostname}.md"
            doc_text = pyavd.get_device_doc(structured_config, add_md_toc=True)

            with open(doc_file, "w", encoding="utf-8") as f:
                f.write(doc_text)

            generated_files.append(doc_file)
            self.logger.debug("Generated doc: %s", doc_file)

        self.logger.info("Generated %d documentation files", len(generated_files))
        return generated_files

    except DocumentationGenerationError:
        raise
    except Exception as e:
        raise DocumentationGenerationError(f"Failed to generate documentation: {e}") from e

TestGenerator

TestGenerator(test_type: str = 'anta')

Generator for test files (ANTA).

This class handles generation of ANTA test files from AVD inventory data.

Parameters:

Name Type Description Default
test_type str

Test type (‘anta’ or ‘robot’), by default “anta”

'anta'
Source code in avd_cli/logics/generator.py
def __init__(self, test_type: str = "anta") -> None:
    """Initialize the test generator.

    Parameters
    ----------
    test_type : str, optional
        Test type ('anta' or 'robot'), by default "anta"
    """
    self.test_type = test_type
    self.logger = logging.getLogger(f"{__name__}.{self.__class__.__name__}")

generate

generate(
    inventory: InventoryData,
    output_path: Path,
    device_filter: Optional[DeviceFilter] = None,
) -> List[Path]

Generate test files.

Parameters:

Name Type Description Default
inventory InventoryData

Loaded inventory data (should contain ALL devices for proper AVD context)

required
output_path Path

Output directory for generated tests

required
device_filter Optional[DeviceFilter]

Filter to determine which devices to generate tests for, by default None. Note: All devices are used for avd_facts calculation, filter only affects which test files are written.

None

Returns:

Type Description
List[Path]

List of generated test file paths

Raises:

Type Description
TestGenerationError

If generation fails

Source code in avd_cli/logics/generator.py
def generate(
    self, inventory: InventoryData, output_path: Path, device_filter: Optional["DeviceFilter"] = None
) -> List[Path]:
    """Generate test files.

    Parameters
    ----------
    inventory : InventoryData
        Loaded inventory data (should contain ALL devices for proper AVD context)
    output_path : Path
        Output directory for generated tests
    device_filter : Optional[DeviceFilter], optional
        Filter to determine which devices to generate tests for, by default None.
        Note: All devices are used for avd_facts calculation, filter only affects
        which test files are written.

    Returns
    -------
    List[Path]
        List of generated test file paths

    Raises
    ------
    TestGenerationError
        If generation fails
    """
    self.logger.info("Generating %s tests", self.test_type.upper())

    # Create output directory
    tests_dir = output_path / DEFAULT_TESTS_DIR
    tests_dir.mkdir(parents=True, exist_ok=True)

    generated_files: List[Path] = []

    try:
        # Import pyavd for ANTA catalog generation
        try:
            import pyavd
        except ImportError as e:
            raise TestGenerationError(
                "pyavd library not installed. Install with: pip install pyavd"
            ) from e

        # Reuse the conversion logic from ConfigurationGenerator
        config_gen = ConfigurationGenerator(workflow="eos-design")
        # Build inputs from ALL devices (for proper AVD context)
        all_devices = inventory.get_all_devices()
        all_inputs = config_gen._build_pyavd_inputs_from_inventory(inventory, all_devices)

        if not all_inputs:
            self.logger.warning("No devices to process")
            return generated_files

        # Filter out devices without ID (required by pyavd for ANTA catalog)
        all_inputs = self._filter_devices_with_id(all_inputs)

        if not all_inputs:
            self.logger.warning("No devices with valid 'id' to generate tests for")
            return generated_files

        # Generate structured configs for all devices
        structured_configs = self._generate_structured_configs(pyavd, all_inputs)

        # Generate and write ANTA catalog
        catalog_file = self._write_anta_catalog(tests_dir, structured_configs)
        generated_files.append(catalog_file)

        # Generate ANTA inventory file with device information
        inventory_file = tests_dir / "anta_inventory.yml"
        self.logger.info("Generating ANTA inventory file: %s", inventory_file)

        with open(inventory_file, "w", encoding="utf-8") as f:
            f.write(self._generate_anta_inventory(structured_configs, inventory))

        generated_files.append(inventory_file)

        self.logger.info(
            "Generated %d ANTA files (catalog + inventory) with tests for all configured features",
            len(generated_files)
        )
        return generated_files

    except TestGenerationError:
        raise
    except Exception as e:
        raise TestGenerationError(f"Failed to generate tests: {e}") from e

generate_all

generate_all(
    inventory: InventoryData,
    output_path: Path,
    workflow: str = "eos-design",
    device_filter: Optional[DeviceFilter] = None,
) -> Tuple[List[Path], List[Path], List[Path]]

Generate all outputs: configurations, documentation, and tests.

Parameters:

Name Type Description Default
inventory InventoryData

Loaded inventory data (should contain ALL devices for proper AVD context)

required
output_path Path

Output directory for all generated files

required
workflow str

Workflow type, by default “eos-design”

'eos-design'
device_filter Optional[DeviceFilter]

Filter to determine which devices to generate outputs for, by default None. Note: All devices are used for avd_facts calculation, filter only affects which output files are written.

None

Returns:

Type Description
Tuple[List[Path], List[Path], List[Path]]

Tuple of (config_files, doc_files, test_files)

Source code in avd_cli/logics/generator.py
def generate_all(
    inventory: InventoryData,
    output_path: Path,
    workflow: str = "eos-design",
    device_filter: Optional["DeviceFilter"] = None,
) -> Tuple[List[Path], List[Path], List[Path]]:
    """Generate all outputs: configurations, documentation, and tests.

    Parameters
    ----------
    inventory : InventoryData
        Loaded inventory data (should contain ALL devices for proper AVD context)
    output_path : Path
        Output directory for all generated files
    workflow : str, optional
        Workflow type, by default "eos-design"
    device_filter : Optional[DeviceFilter], optional
        Filter to determine which devices to generate outputs for, by default None.
        Note: All devices are used for avd_facts calculation, filter only affects
        which output files are written.

    Returns
    -------
    Tuple[List[Path], List[Path], List[Path]]
        Tuple of (config_files, doc_files, test_files)
    """
    config_gen = ConfigurationGenerator(workflow=normalize_workflow(workflow))
    doc_gen = DocumentationGenerator()
    test_gen = TestGenerator()

    configs = config_gen.generate(inventory, output_path, device_filter)
    docs = doc_gen.generate(inventory, output_path, device_filter)
    tests = test_gen.generate(inventory, output_path, device_filter)

    return configs, docs, tests