Skip to content
17 Jan 2025

Generators and Services in Infrahub – Part 3

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, like site_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

And then a similar setup for the query:

  • 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!

Here we can see the output from the console of running the generator for site Chicago

infrahubctl generator --branch "generator-test" site_generator site_name=Chicago
Infrahub Generator output example

And now we can see our devices from inside the Infrahub UI.
Infrahub screenshot: devices from Generator

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:

Jordan Villarreal

January 17, 2025

REQUEST A DEMO

See what Infrahub can do for you

Get a personal tour of Infrahub Enterprise

Learn how we can support your infrastructure automation goals

Ask questions and get advice from our automation experts

By submitting this form, I confirm that I have read and agree to OpsMill’s privacy policy.

Fantastic! 🙌

Check your email for a message from our team.

From there, you can pick a demo time that’s convenient for you and invite any colleagues who you want to attend.

We’re looking forward to hearing about your automation goals and exploring how Infrahub can help you meet them.