Extending the pipeline
The touch-point lists below give you the exact files to edit for the three most common extensions.
Native schema vs. the escape hatchβ
When closing a capability gap (for example, to support a new AVD example scenario), decide up front whether to model it natively in the schema or pass it through the avd_custom_hostvars escape hatch:
- Prefer a native schema change when the capability is reused across more than one scenario, is a first-class topology/role/protocol concept operators select in the UI, or needs validation, pool allocation, or deterministic generation. Examples: device roles and their
ROLE_TO_AVD_TYPEmapping, underlay protocol choices, EVPN inputs such asevpn_vlan_aware_bundlesand EVPN Gateway Groups. - Use the
avd_custom_hostvarsescape hatch when the capability is specific to a single scenario, is a pass-through of PyAVD keys that need no allocation or cross-device derivation, or would be premature to model before real demand. Examples: campus dot1x/PoE/port-profiles/in-band management and MPLS/VPN-IPv4 for ISIS-LDP IPVPN.
avd_custom_hostvars is a JSON attribute available at fabric, pod, and device scope. Its content deep-merges with the generator-produced hostvars, and generator-produced values win on conflict. Keep escape-hatch content in committed seed data (not manual UI edits) so a design stays reproducible and idempotent, and confirm every key is accepted by the pinned PyAVD version. Escape-hatch use is a deliberate, documented choice per capability β not a default fallback to avoid modeling.
Add a new device roleβ
Scenario: you want to support a new Infrahub role (for example, border-leaf) that maps to a PyAVD type.
Touch points:
-
Schema β add the role value to the
DcimDeviceroledropdown inschemas/dcim_extensions.yml(the single authoritative device-role list). -
Reload the schema and regenerate generated files β none of these files should be hand-edited:
uv run invoke load-schema # push schema to Infrahubuv run infrahubctl graphql export-schema --destination schema.graphql # refresh the local GraphQL SDLuv run infrahubctl protocols --schemas schemas --out src/solution_arista_avd/protocols.py # refresh typed protocol classes -
Role map β add the mapping in
src/solution_arista_avd/avd.py:ROLE_TO_AVD_TYPE: dict[str, str] = {...,"border_leaf": "l3leaf", # or whatever PyAVD type fits} -
Hostvars generator β add a branch for the new role in
generators/generate_avd_device_hostvar.pyfor any role-specific fields (uplink role, MLAG, EVPN data). -
Upstream generator β whichever generator creates devices of this role (fabric/pod/rack/custom) needs to set the
roleattribute correctly and add the device to theavd_devicesgroup. -
Tests β add a case in
tests/unit/test_avd.pycoveringget_avd_type("border_leaf"). -
Docs β update Role Mapping and, if the role implies new hostvar fields, Hostvars Reference.
Add a new transform outputβ
Scenario: you want an additional artifact per device or per fabric (for example, a JSON summary, a CSV inventory).
Touch points:
-
GraphQL query β write the
.gqlquery undertransforms/. Example:transforms/avd_inventory.gql. -
Pydantic query model β do not write this manually. Generate it with:
uv run infrahubctl graphql generate-return-types transforms/avd_inventory.gqlThis reads
schema.graphql(checked in at the repo root) and emitstransforms/avd_inventory_query.pyalongside the query. Re-run whenever the query or the schema changes. If the schema is stale, regenerate it first withuv run infrahubctl graphql export-schema(requires a running Infrahub). -
Transform class β implement the transform in
transforms/avd_inventory.pyas a subclass of the Infrahub Python transform base class. Typical structure:class AvdInventoryTransform(InfrahubTransform):query = "avd_inventory"async def transform(self, data: dict) -> str:parsed = AvdInventoryQuery.model_validate(data)# ... your logic herereturn output -
Register in
.infrahub.yml:queries:- name: avd_inventoryfile_path: "./transforms/avd_inventory.gql"python_transforms:- name: avd_inventoryclass_name: AvdInventoryTransformfile_path: "./transforms/avd_inventory.py"artifact_definitions:- name: avd_inventory_csvtargets: fabrics # or avd_devices, depending on scopetransformation: avd_inventory -
Tests β add a unit test under
tests/unit/exercisingtransform()on a fixture, plus optionally an integration test that hits a running Infrahub.
After merge, operators can open the new artifact from the target node's Artifacts tab.
Add a new field to hostvarsβ
Scenario: you want PyAVD to receive an additional input field (for example, a per-device SNMP location string) that currently isn't populated.
Touch points:
-
Schema β if the field isn't already represented, add it to the relevant schema (
DcimDevice,NetworkFabric, etc.) inschemas/. -
Reload the schema and regenerate generated files:
uv run invoke load-schema # push schema to Infrahubuv run infrahubctl graphql export-schema --destination schema.graphql # refresh the local GraphQL SDLuv run infrahubctl protocols --schemas schemas --out src/solution_arista_avd/protocols.py # refresh typed protocol classes -
GraphQL query β update
generators/avd_device_hostvar.gqlto pull the new field. -
Pydantic query model β regenerate, don't hand-edit:
uv run infrahubctl graphql generate-return-types generators/avd_device_hostvar.gqlThis rewrites
generators/generate_avd_device_inputs_query.pyfrom the query and the refreshed schema. -
Hostvars builder β map the new attribute into the PyAVD hostvars dict in
generators/generate_avd_device_hostvar.py:- Device-level, role-independent field β add it in
_build_hostvars()(wheretype,fabric_name,bgp_as, loopback/mgmt basics are assembled). - Role-specific or multi-attribute field β add the logic in the appropriate role branch of the same file.
- Device-level, role-independent field β add it in
-
Validation β PyAVD's
validate_inputs()flags unknown fields as errors. Confirm the field is in the PyAVD input schema for the version pinned (see overview). If it isn't a standard PyAVD field, look at usingcustom_structured_configuration_prefixorstructured_configpass-through instead. -
Tests β add a case in
tests/unit/test_hostvar_ordering.pyfor any hostvars logic added to the generator. (tests/unit/test_avd.pycovers only the roleβtype mapping insrc/solution_arista_avd/avd.py.) -
Docs β update Hostvars Reference with the new field and its Infrahub source.
Checklist: what to run before opening a PRβ
uv run invoke lintβ ruff, mypy, yamllint must all pass.uv run pytest tests/unitβ all unit tests pass.uv run pytest tests/integrationβ integration tests pass (requires a running Infrahub).- On a feature branch in a live Infrahub, trigger the affected generators twice and confirm idempotence β the second run should be a no-op per Debugging the Pipeline β checksum-based skipping.