In this part of our series on generators and services, we’re diving into the practical aspects of setting up a generator. In our last video, we covered the planning phase, including identifying use cases, defining deliverables, and mapping workflows. This time, we’ll focus on the actual coding and configuration needed in InfraHub to get our generator up and running.
Setting the Generator Up
To start, we need to define our generator within the .infrahub.yaml
file. This is crucial as it outlines how the different components come together. Our definition file will specify the locations of the Python code and the GraphQL query required to pull the data we need for our generator to function.
Walking through our generator definition:
name:
We set a name for our generator, likesite_generator
.- file_path: This points to where our generator code resides.
- targets: We define our targets, which will be set up in a future video.
- query: We specify the query name, which is used further in the file, and the path to the query file.
- class_name: We define a name for the class to use in the generator
- name: The name we use to point at the query
- file_path: This points to where our query file resides.
---
generator_definitions:
- name: site_generator
file_path: "generators/site_generator.py"
targets: "generator_sites"
query: "site_details"
class_name: "SiteGenerator"
queries:
- name: site_details
file_path: "queries/site_details.gql"
GraphQL Query
The GraphQL query will filter out the necessary information for our generator. We will focus on returning the following variable values for our site:
edges > node > name:
This will return the name of the site we are targeting.edges > node > site_prefix:
This return the site prefix for device and interface names.edges > node > homing:
Whether the site is single or dual-homed.
query site_details($site_name: String!) {
LocationSite(name__value: $site_name) {
edges {
node {
name {
value
}
site_prefix {
value
}
homing {
value
}
}
}
}
}
Generator File
This is where the magic happens. The generator is coded with Python and uses the Infrahub SDK to bring the logic to life. I’ll use comments within the Python code to walk through the flow:
import logging
from infrahub_sdk.generator import InfrahubGenerator
# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# Suppress httpx logs for ease of viewing on the console
httpx_logger = logging.getLogger("httpx")
httpx_logger.setLevel(logging.WARNING)
# Call our SiteGenerator as defined in our .infrahub.yml file
class SiteGenerator(InfrahubGenerator):
##### Receive the output of the GraphQL query
async def generate(self, data: dict) -> None:
logger.info("Received data for processing.")
if not data["LocationSite"]["edges"]:
logger.warning("No sites found in query result.")
return
######### Extract site details we need to build our devices
site = data["LocationSite"]["edges"][0]["node"]
site_name = site["name"]["value"]
site_prefix = site["site_prefix"]["value"]
homing = site["homing"]["value"]
logger.info(f"Processing site: {site_name} with prefix: {site_prefix}, Homing: {homing}")
######### Fetch the Management IP Pool
management_pool = await self.client.get(
kind="CoreIPAddressPool",
any__value="Management Pool",
raise_when_missing=False
)
if not management_pool:
logger.error("Management Pool not found. Ensure it is created in the GUI.")
return
logger.info(f"Using IPAM Pool: {management_pool}")
######### Define device configuration based on homing type
device_config = {
"single_homed": [
{"name": f"{site_prefix}-router-01", "role": "core", "type": "router"},
{"name": f"{site_prefix}-firewall-01", "role": "firewall", "type": "firewall"},
{"name": f"{site_prefix}-switch-01", "role": "end_user_switch", "type": "switch"},
],
"dual_homed": [
{"name": f"{site_prefix}-router-01", "role": "core", "type": "router"},
{"name": f"{site_prefix}-router-02", "role": "core", "type": "router"},
{"name": f"{site_prefix}-firewall-01", "role": "firewall", "type": "firewall"},
{"name": f"{site_prefix}-firewall-02", "role": "firewall", "type": "firewall"},
{"name": f"{site_prefix}-switch-01", "role": "end_user_switch", "type": "switch"},
],
}
devices = device_config.get(homing, [])
######### Begin creating the objects for our service of a new site
for device in devices:
device_name = device["name"]
device_role = device["role"]
device_type = device["type"]
############# Create device
logger.info(f"Creating device: {device_name} ({device_role})")
device_obj = await self.client.create(
kind="InfraDevice",
data={
"name": device_name,
"role": device_role,
"type": device_type,
"status": "active",
"site": site_name,
},
)
await device_obj.save(allow_upsert=True)
############# Create interfaces for device
for iface_num in range(1, 3):
interface_name = f"{device_name}-iface{iface_num}"
logger.info(f"Adding interface: {interface_name}")
interface = await self.client.create(
kind="InfraInterfaceL3",
data={
"name": interface_name,
"device": device_obj,
"speed": 1000,
"status": "active",
"role": "management" if iface_num == 1 else "uplink",
},
)
await interface.save(allow_upsert=True)
############# Obtain and associate IP address for management interfaces
ip_address = await self.client.allocate_next_ip_address(
resource_pool=management_pool,
identifier=f"{interface_name}-ip",
data={"description": f"IP for {interface_name}"}
)
ip_address.interface = interface
await ip_address.save(allow_upsert=True)
logger.info(f"Allocated IP: {ip_address.address.value} and linked to {interface_name}")
logger.info("Device and interface creation completed.")
Let's Run It!
infrahubctl generator --branch "generator-test" site_generator site_name=Chicago
And now we can see our devices from inside the Infrahub UI
With the generator functioning as expected, we now look towards operationalizing it. This means integrating the generator into the application for easier consumption by users.
Conclusion
With our next installment in the series, we will implement a way to control when generators run and against which sites. This will allow for more flexibility and prevent unnecessary executions against all sites.
We will also be bringing the generator, and all of the required files, into a git repository so we can operationalize its usage. We’ll also look at how to leverage our proposed changes feature to help teams even more.
To learn more about Generators today, check out the following links to our documentation:
- Documentation > Topics > Generator
- Documentation > Infrahub Overview > Generators
- Documentation > Guides > Creating a Generator
- Documenation > infrahubctl > infrahubctl generator