network device management blog

Tackling the complexities of enterprise inventories

One common challenge our customers face is the need to track hosts from multiple sources: LDAP, cloud providers, and enterprise CMDB systems. Using a dynamic inventory allows users to integrate with these systems and update the Ansible inventory as it varies over time, with hosts spinning up and shutting down in response to business demands.

Ansible supports two ways to connect with external inventory: Inventory plugins and inventory scripts. 

Today we are going to cover dynamic inventory plugins as a Collection for network device management through an /etc/hosts file. This same type of setup can be used for creating any dynamic inventory using different items from /etc/hosts files to ini files or even csv’s. 

The first mission: Where is the source of truth?

We are going to start by figuring out the source of truth of the inventory we want to import. 

If you want to test and use this inventory plugin you can find the code in this Github repository: 

https://github.com/jmcleroy/inventoryplugin.git

In this case, it will be an /etc/hosts file externally stored in the Github/Gitlab inventory plugin repo as a test, in a similar fashion this file can also be pulled from a server with the correct host file with all of your network devices. In this Git repo we are going to make the necessary changes to permit a multi-vendor setup and we will use keywords from the naming to trigger grouping capabilities.

The hosts file will look like this:

% cat hosts 
10.25.25.25 ansible.cisco.external
10.25.26.26 ansible2.juniper.external
10.26.35.52 ansible3.arista.external

From there we will have the hierarchy for the Collections plugin. This is new to Ansible starting in 2.11+ and Red Hat Ansible Automation Platform 2.1.

The hierarchical structure looks like this:

inventoryplugin % tree 
.
├── README.md
├── collections
│   └── ansible_collections
│       └── ansible
│           └── network
│               └── plugins
│                   └── inventory
│                       └── etc_hosts.py
├── etc_hosts.yml
└── hosts
6 directories, 4 files

The second mission: Using the plugin

After we define our source of truth, we have to add the playbook to call this new plugin. There are some items you need to set, however within the ansible.cfg, you will need to set the Collections path see following code:

#
#inject_facts_as_vars = True
# Paths to search for collections, colon separated
collections_paths = ~/.ansible/collections:/usr/share/ansible/collections:/Users/jmcleroy/PycharmProjects/inventoryplugin/collections
# Paths to search for roles, colon separated
#roles_path = ~/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles
# Host key checking is enabled by default
host_key_checking = False

Also you will need to set the inventory path see below:

# set plugin path directories here, separate with colons
#action_plugins     = /usr/share/ansible/plugins/action
#become_plugins     = /usr/share/ansible/plugins/become
#cache_plugins      = /usr/share/ansible/plugins/cache
#callback_plugins   = /usr/share/ansible/plugins/callback
#connection_plugins = /usr/share/ansible/plugins/connection
#lookup_plugins     = /usr/share/ansible/plugins/lookup
inventory_plugins  = /usr/share/ansible/plugins/inventory:/Users/jmcleroy/PycharmProjects/inventoryplugin/collections/ansible_collections/ansible/network/plugins/inventory
#vars_plugins       = /usr/share/ansible/plugins/vars
#filter_plugins     = /usr/share/ansible/plugins/filter
#test_plugins       = /usr/share/ansible/plugins/test
#terminal_plugins   = /usr/share/ansible/plugins/terminal
#strategy_plugins   = /usr/share/ansible/plugins/strategy

Here is a snippet of the Python plugin:

DOCUMENTATION = '''
    name: Etc_Hosts Inventory
    plugin_type: inventory
    author:
      - Eric McLeroy (@jmcleroy)
    short_description: Dynamic inventory plugin for a etc/hosts network file.
    version_added: "n/a"
    extends_documentation_fragment:
      - constructed
    options:
        plugin:
            description: Token that ensures this is a source file for the plugin.
            required: True
            choices: ['ansible.network.etc_hosts']
        file_path:
            description:
                - The path to the etc/hosts file.
                - This can be either an absolute path, or relative to inventory file.
            required: True
    requirements:
        - python >= 2.7
'''
EXAMPLES = r'''
# example etc_hosts.yml file
---
plugin: ansible.network.etc_hosts
file_path: /etc/hosts
'''
 
from ansible.errors import AnsibleError, AnsibleParserError
from ansible.plugins.inventory import BaseFileInventoryPlugin, Constructable
from string import digits
 
import os
import re
 
 
class InventoryModule(BaseFileInventoryPlugin, Constructable):
 
    NAME = 'ansible.network.etc_hosts'
 
    def verify_file(self, path):
        super(InventoryModule, self).verify_file(path)
        return path.endswith(('etc_hosts.yml', 'etc_hosts.yaml'))
 
    def parse(self, inventory, loader, path, cache=True):
        super(InventoryModule, self).parse(inventory, loader, path)
        self._read_config_data(path)
 
        hosts_file_in = self.get_option('file_path')
        if os.path.isabs(hosts_file_in):
            hosts_file = hosts_file_in
        else:
            hosts_file = os.path.join(os.path.dirname(path), hosts_file_in)
        file=open(hosts_file, 'r')
        lines=file.readlines()
        for line in lines:
            group_name = line.split(' ')[1]
            group_name = re.split("\.|-", group_name)[1].rstrip('0123456789')
            self.inventory.add_group(group_name)
            host_name = self.inventory.add_host(line.split(' ')[1].strip(), group_name)
            self.inventory.set_variable(host_name, 'ansible_host' , line.split()[0])

Next we will use the plugin to show the output as created by the plugin that took the ini file from above and dynamically created groups from a variable.

[emcleroy@rhel3 etcinventory]$ ansible-inventory --inventory etc_hosts.yml --list
{
    "_meta": {
        "hostvars": {
            "ansible.cisco.external": {
                "ansible_host": "10.25.25.25"
            },
            "ansible2.juniper.external": {
                "ansible_host": "10.25.26.26"
            },
            "ansible3.arista.external": {
                "ansible_host": "10.26.35.52"
            }
        }
    },
    "all": {
        "children": [
            "arista",
            "cisco",
            "juniper",
            "ungrouped"
        ]
    },
    "arista": {
        "hosts": [
            "ansible3.arista.external"
        ]
    },
    "cisco": {
        "hosts": [
            "ansible.cisco.external"
        ]
    },
    "juniper": {
        "hosts": [
            "ansible2.juniper.external"
        ]
    }
}

What can I do next?

You can view a step by step example on how to use the plugin in the following video demonstration:

Whether you are beginning your automation journey or are a seasoned veteran, there are a variety of resources to enhance your automation knowledge:


저자 소개

Eric is a Principal Specialist Solutions Architect at Red Hat, focused on Ansible Network for the TME organization. He is ever expanding his knowledge of networking and Ansible and how it can help Red Hat customers solve more complex issues using Red Hat Ansible Automation Solution.
UI_Icon-Red_Hat-Close-A-Black-RGB

채널별 검색

automation icon

오토메이션

기술, 팀, 인프라를 위한 IT 자동화 최신 동향

AI icon

인공지능

고객이 어디서나 AI 워크로드를 실행할 수 있도록 지원하는 플랫폼 업데이트

open hybrid cloud icon

오픈 하이브리드 클라우드

하이브리드 클라우드로 더욱 유연한 미래를 구축하는 방법을 알아보세요

security icon

보안

환경과 기술 전반에 걸쳐 리스크를 감소하는 방법에 대한 최신 정보

edge icon

엣지 컴퓨팅

엣지에서의 운영을 단순화하는 플랫폼 업데이트

Infrastructure icon

인프라

세계적으로 인정받은 기업용 Linux 플랫폼에 대한 최신 정보

application development icon

애플리케이션

복잡한 애플리케이션에 대한 솔루션 더 보기

Virtualization icon

가상화

온프레미스와 클라우드 환경에서 워크로드를 유연하게 운영하기 위한 엔터프라이즈 가상화의 미래