How to Configure Schema Mappings
This guide shows you how to configure schema mappings to control how Infrahub data translates into Nornir inventory properties. Schema mappings are the bridge between your Infrahub data model and Nornir's expected host attributes.
What you'll accomplish
By following this guide, you'll be able to:
- Map Infrahub node attributes to Nornir host properties
- Handle nested relationships and complex data structures
- Create custom mappings for specialized use cases
- Debug and troubleshoot mapping issues
Prerequisites
- Working Nornir-Infrahub installation
- Understanding of your Infrahub schema (see example schema in Getting Started)
- Access to Infrahub GraphQL explorer (for testing queries)
Understanding schema mappings
Schema mappings define how to extract values from Infrahub nodes and assign them to Nornir host properties. Each mapping consists of:
name: The Nornir property to set (i.e., "hostname", "platform")mapping: The path to the value in the Infrahub node (i.e., "primary_address.address")
Step 1: Identify required Nornir properties
First, determine which Nornir properties your automation tasks need:
| Property | Purpose | Common Mapping Source |
|---|---|---|
hostname | IP/FQDN for connections | Device primary address |
platform | Device OS type | Platform or OS attribute |
username | Authentication user | Credential or static value |
password | Authentication password | Credential or static value |
port | Connection port | Interface or static value |
data | Custom data | Any Infrahub attributes |
Step 2: Explore your Infrahub schema
Use the Infrahub GraphQL explorer to understand available attributes. Based on the example schema from the Getting Started guide, you can query:
query {
InfraDevice {
edges {
node {
name {
value
}
primary_address {
node {
address {
value
}
}
}
platform {
node {
name {
value
}
nornir_platform {
value
}
}
}
site {
node {
name {
value
}
}
}
role {
node {
name {
value
}
}
}
}
}
}
}
This helps you identify the exact paths to your data based on your schema structure.
Step 3: Configure basic mappings
Basic attribute mapping
For direct attributes on the node:
schema_mappings:
- name: "hostname"
mapping: "name" # Maps device name to hostname
Nested relationship mapping
For attributes through relationships (based on the example schema):
schema_mappings:
- name: "hostname"
mapping: "primary_address.address" # Traverses to InfraIPAddress node
- name: "platform"
mapping: "platform.nornir_platform" # Traverses to InfraPlatform node
Multiple level nesting
For deeply nested data:
schema_mappings:
- name: "location"
mapping: "site.location.name" # Multiple relationships
Step 4: Handle complex mappings
IP address handling
Infrahub stores IP addresses as interface objects. The integration automatically extracts the IP:
schema_mappings:
- name: "hostname"
mapping: "primary_address.address" # Automatically converts IPHost to string
Handling missing attributes
When a mapping fails (for example, relationship doesn't exist), it's silently skipped:
schema_mappings:
- name: "hostname"
mapping: "primary_address.address" # Skipped if primary_address is not set
- name: "management_ip"
mapping: "primary_address.address" # Only sets hostname if relationship exists
Note: The plugin does not support fallback mappings. Each mapping is independent.
Custom data mappings
Additional attributes from Infrahub nodes are stored in the host's data dictionary:
def example_task(task):
# Access the Infrahub node data
infrahub_node = task.host.data["InfrahubNode"]
# Access node attributes directly
if hasattr(infrahub_node, 'serial_number'):
serial = infrahub_node.serial_number.value
print(f"Device {task.host.name} serial: {serial}")
Note: Custom schema mappings create Nornir host properties for standard attributes like hostname and platform. Additional node data is accessible through the InfrahubNode object.
Step 5: Configure group mappings
Group mappings create dynamic Nornir groups from Infrahub relationships:
group_mappings:
- "site.name" # Creates groups like "site__chicago"
- "role.name" # Creates groups like "role__spine"
- "platform.name" # Creates groups like "platform__eos"
Groups are automatically prefixed with the relationship name to avoid conflicts.
Step 6: Test your mappings
Create a test script to verify mappings:
from nornir import InitNornir
import json
def test_mappings():
nr = InitNornir(config_file="config.yaml")
# Check a specific host
if "router1" in nr.inventory.hosts:
host = nr.inventory.hosts["router1"]
print(f"Host: {host.name}")
print(f"Hostname: {host.hostname}")
print(f"Platform: {host.platform}")
print(f"Groups: {[g.name for g in host.groups]}")
print(f"Data: {json.dumps(host.data, indent=2)}")
# List all groups
print("\nAll groups:")
for group in nr.inventory.groups:
print(f" - {group}")
if __name__ == "__main__":
test_mappings()
Validation
Check mapping success
After configuration, verify:
- All hosts have required properties set
- Groups are created as expected
- No mapping errors in logs
Common validation commands
# Check all hosts have hostname
for host in nr.inventory.hosts.values():
assert host.hostname is not None, f"{host.name} missing hostname"
# Verify platform mappings
platforms = {h.platform for h in nr.inventory.hosts.values()}
print(f"Discovered platforms: {platforms}")
# Check group membership
for group in nr.inventory.groups.values():
print(f"{group.name}: {len(group.hosts)} hosts")
Advanced usage
Using mapping results in tasks
Access mapped data in your tasks:
def device_info(task):
# Access standard properties
print(f"Connecting to {task.host.hostname}")
print(f"Platform: {task.host.platform}")
# Access custom mapped attributes
if hasattr(task.host, 'serial_number'):
print(f"Serial: {task.host.serial_number}")
# Access Infrahub node
node = task.host.data["InfrahubNode"]
print(f"Infrahub ID: {node.id}")
Dynamic mapping based on node type
For environments with multiple device types:
# Router-specific mappings
host_node:
kind: "InfraRouter"
schema_mappings:
- name: "hostname"
mapping: "loopback0.address"
- name: "platform"
mapping: "platform.nornir_platform"
# Switch-specific mappings (different config file)
host_node:
kind: "InfraSwitch"
schema_mappings:
- name: "hostname"
mapping: "management_ip.address"
- name: "platform"
mapping: "platform.nornir_platform"
Troubleshooting mapping failures
If mappings fail silently:
- Enable debug logging:
import logging
logging.basicConfig(level=logging.DEBUG)
- Check the Infrahub GraphQL query:
# Add to your script
print(nr.inventory.hosts["device1"].data["InfrahubNode"]._raw_data)
- Verify relationship cardinality:
- Single relationships:
mapping: "platform.name" - Many relationships not supported directly