Template and Plugin Expansion Walkthrough
This walkthrough shows how the render engine selects entrypoint templates, expands Jinja inheritance, and loads external template plugins. It is intended for external users who need to build their own template plugins while reusing the Config Manager template engine.
Entrypoint Selection
For each device, the selected DCIM provider loads one RenderData payload with
device, location, and optional plugin data. The renderer then selects every
entrypoint below this logical path:
The platform, role, and intended-firmware values come from provider-supplied render data and are normalized by lower-casing and replacing whitespace with hyphens. The bundled Nautobot provider currently maps intended firmware from its configuration context; that is not a requirement for another provider.
For a device with platform Cumulus Linux, role Storage Leaf, and intended firmware 5.16.1, the renderer looks under:
Each matching .j2 file renders one output file. The render service stores the output by stripping the path and .j2 suffix, so entrypoint/startup.yaml.j2 becomes startup.yaml.
Built-In Expansion Example
A Cumulus storage leaf startup entrypoint starts as a thin version-specific composition file:
The role base inherits from broader common behavior:
The common base defines the output skeleton and required blocks:
The final render is the merged result of those layers:
- The version-specific entrypoint selects the firmware-specific overrides.
- The role base supplies role-level blocks and includes.
- The common base supplies the file skeleton and default blocks.
- Jinja replaces each
includewith the included template output. - Filters convert provider-supplied device, location, and plugin data into the values used by the templates.
Plugin Discovery
Template plugins are normal Python packages. A plugin registers an entry point in the nv_config_manager_templates.plugins group:
The plugin module can provide any combination of templates, filters, or provider-neutral render-data requirements:
At renderer startup, Config Manager:
- Discovers installed entry points from
nv_config_manager_templates.plugins. - Calls plugin hooks such as
get_template_paths(),get_custom_filters(), andget_render_data_requirements(). - Adds plugin template paths before the built-in package templates.
- Loads built-in filters.
- Loads plugin filters that do not conflict with an existing filter name.
- Collects provider-neutral plugin data requirements. The selected DCIM provider owns obtaining and normalizing matching data before it invokes the renderer.
Because plugin template paths are loaded before the built-in templates, a plugin can add new logical paths or intentionally shadow a built-in template path. Shadowing should be explicit and covered by render tests. Prefer extends and small block overrides when the plugin only needs to adjust part of a built-in template.
Plugin Template Layout
A plugin template root uses the same logical layout as the built-in templates:
For a new device role, create a role path that matches the normalized role name. For example, role Example Storage Leaf resolves to example-storage-leaf.
An entrypoint can reuse a built-in base template and override only the blocks that differ:
If the plugin needs shared behavior for multiple plugin-owned roles, use plugin-owned common role names instead of broad names that look built-in:
Plugin Data and Filters
The render context always includes:
When the selected provider supplies plugin data, the context can also include:
Plugin data is keyed by requirement name. Templates should not traverse raw provider response paths directly. Instead, expose stable domain concepts through plugin filters:
Then call that filter from the template:
This keeps provider model changes isolated to Python filters instead of spreading response-shape assumptions across templates.
Local Plugin Testing
Install the plugin into the same Python environment as nv-config-manager-templates, then use template-cli for local iteration.
Create a provider configuration file, for example example-provider.toml:
Cache the complete provider-supplied render data:
Render from cached data:
Without --vault, template-cli render disables Vault lookups and returns placeholder secret values. Use this mode for render tests, not for production device configuration.
For regression coverage, add a portable RenderData cache and expected
rendered output for every plugin role and entrypoint. Template tests do not
accept split or native DCIM cache formats.
The template library does not know a provider’s REST or GraphQL APIs. When a
plugin needs extension data, declare get_render_data_requirements() and work
with the provider author to populate the matching plugin_data value. See
Contribute a DCIM Provider
for the provider-side contract.
Deployment
External deployments load template plugins through the installer or Helm chart, not through template-cli.
With the installer, add plugin directories or plugin tarballs on the Template Plugins screen. The installer stages that content into the render service template plugin PVC and restarts render workloads only when the staged template content changes.
For Helm or GitOps-managed deployments, set renderService.templatePlugins.enabled and provide plugin source images when needed. A plugin source image can provide:
The render service installs those plugin wheels or source packages into its runtime environment so their nv_config_manager_templates.plugins entry points are discoverable.
Rendered configuration metadata records both the engine package version and installed plugin package versions:
That version vector lets Config Manager compare rendered configurations against the exact template engine and plugin set that produced them.