Ansible Constructed Inventory

Blog Detail

Ansible’s inventory management system is one of the strongest features of the platform. The options for using different inventory sources make it quite extensible. Starting with a static inventory of ini or yaml files can be fine for a small deployment or proof-of-concept, but this only scales so far. To scale beyond that, you will likely need to use a dynamic inventory plugin or script. With all the plugins that are available today, it is often better to go with one designed to work with your dynamic inventory source, but keeping in mind, that plugins are limited in their capability as-is.

In the case of the NetBox inventory plugin, there are several options out of the box by which to group hosts using different data points already available within the application (docs here):

  sites  
  site  
  tenants  
  tenant  
  racks  
  rack  
  rack_group  
  rack_role  
  tags  
  tag  
  device_roles  
  role  
  device_types  
  device_type  
  manufacturers  
  manufacturer  
  platforms  
  platform  
  region  
  cluster  
  cluster_type  
  cluster_group  
  is_virtual  
  services  

These are great options for a lot of use cases, but what if there is a need to group hosts by something that is not supported natively? Well, any inventory plugin can be extended with the functionality of the Ansible constructed builtin (docs here). Custom groupings can be created, using either the groups (which is based on a boolean) or the keyed_groups (which is based on naming groups using Jinja2 logic) parameters to dynamically assign hosts into groups… that probably does not mean much right now, but this will be demonstrated shortly by using the keyed_groups option.

Simple Example

As a simple example, perhaps it is necessary to group on the network OS, which is stored in NetBox under the platform key, but a different group name is needed. It is possible to use the keyed_groups parameter to create groups with a custom prefix, network_os in this example netbox_inventory.yml file:


keyed_groups:
  - key: platform
    prefix: "network_os"
    separator: "_"

With this configuration, since all the devices in this instance are Cisco IOS, they are in a single group, named network_os_ios


brandomando@brandomando:$ ansible-inventory -i netbox_inventory.yml --graph
@all:
  |--@network_os_ios:
  |  |--LVO-RTR-01
  |  |--RVA-RTR-01
  |  |--LVO-SWI-01
  |  |--RVA-SWI-01
  |--@ungrouped:

Here you can see that the result is equivalant to 'network_os' + '_' + platform which dynamically created a group called network_os_ios.

Slightly More Complex Jinja Filter Example

This functionality can be extended with any Python code by building a custom filter. Let’s see what that looks like.

One attribute of a device model that you may want to group on is device family, but that is not one of the natively available options, since this is not a field in NetBox. A simple filter can be used to map the device_type to a device family, which can then be used to dynamically assign hosts to their associated family group.


class FilterModule(object):

    def filters(self):
        return {
            'devicetype_family': self.devicetype_family
        }

    def devicetype_family(self, device_type):

        devicetype_map = {
            'ISR1921': 'isr1k', 
            'ISR1941': 'isr1k', 
            'ISR4321': 'isr4k', 
            'ISR4331': 'isr4k', 
            'ws-c3560cx-12pc-s': 'cat3k', 
            'ws-c3650-24ps': 'cat3k', 
        }

        return devicetype_map[device_type]

In the NetBox nb_inventory plugin configuration file, we can use the keyed_groups functionality, in combination with the custom filter, to create a dynamic inventory group assignment.


keyed_groups:
  - key: device_type | devicetype_family
    prefix: "family"
    separator: "_"

By running the keyed_groups key through the devicetype_family filter, the Ansible dynamic inventory will now have custom groups that are dynamically assigned based on the device_type parameter returned by NetBox


brandomando@brandomando:$ ansible-inventory -i netbox_inventory.yml --graph
@all:
  |--@family_cat3k:
  |  |--LVO-SWI-01
  |  |--RVA-SWI-01
  |--@family_isr1k:
  |  |--LVO-RTR-01
  |--@family_isr4k:
  |  |--RVA-RTR-01
  |--@role_rtr:
  |  |--LVO-RTR-01
  |  |--RVA-RTR-01
  |--@role_swi:
  |  |--LVO-SWI-01
  |  |--RVA-SWI-01
  |--@site_lvo:
  |  |--LVO-RTR-01
  |  |--LVO-SWI-01
  |--@site_rva:
  |  |--RVA-RTR-01
  |  |--RVA-SWI-01
  |--@ungrouped:

Compose Example

In addition to being able to group by custom logic, we can also use filters within the compose section of the NetBox inventory plugin config file to set custom variables in our rendered inventory as well. Let’s use the same filter plugin in this example.


compose:
  ansible_network_os: platform.slug
  family: device_type.slug | devicetype_family

Looking at an ansible-inventory output for one of the hosts, we can see that ansible_network_os is now dynamically assigned using the platform parameter from NetBox, and the family variable is now set by our filter.


brandomando@brandomando:$ ansible-inventory -i ./lib/netbox_inventory.yml --host LVO-RTR-01 -y
ansible_host: 10.100.0.5
ansible_network_os: ios  <--- generated from "platform.slug"
custom_fields: {}
device_type: isr4331
family: isr4k            <--- generated from "device_type.slug | devicetype_family"
is_virtual: false
manufacturer: cisco
platform: ios
primary_ip4: 10.100.0.5
regions:
- las-vegas-nv
- us-west
- united-states
- north-america
role: rtr
services: []
site: lvo
tags: []

These options are all features from the Ansible builtin constructed inventory plugin, which the NetBox community netbox.netbox.nb_inventory plugin extends.

Full Example

Demonstrating all of the components together:

plugin: netbox.netbox.nb_inventory
api_endpoint: http://localhost:8000
validate_certs: True
config_context: False

group_by:
  - device_roles
device_query_filters:
  - has_primary_ip: 'true'

keyed_groups:
  - key: platform
    prefix: "network_os"
    separator: "_"
  - key: device_type | devicetype_family
    prefix: "family"
    separator: "_"

compose:
  ansible_network_os: platform.slug
  family: device_type.slug | devicetype_family

Conclusion

This functionality is great for both users of the plugin and the plugin writers. The plugin writer no longer has to consider every possible use case that a user might have. The user can leverage the extensibility that is provided by the Ansible plugin framework and can be used for any number of customized groupings or variable assignments within dynamic inventories.

-Brandon



ntc img
ntc img

Contact Us to Learn More

Share details about yourself & someone from our team will reach out to you ASAP!