From 719c0b00c5b21f200e6ad14de3ac54b2b2dfd975 Mon Sep 17 00:00:00 2001
From: Max Gautier <mg@max.gautier.name>
Date: Tue, 26 Nov 2024 14:34:40 +0100
Subject: [PATCH] Remove the inventory_builder script

This only really help with the easiest part of building your inventory
(listing the hosts) as you still need to edit your groups vars and
similar.
The opaqueness of the script does not really help our users to
understand their own inventory.

Furthermore, there is not really a reason that something which is common
to all the Ansible ecosystem should be done in a special way for
Kubespray.
---
 contrib/inventory_builder/inventory.py        | 480 --------------
 contrib/inventory_builder/requirements.txt    |   3 -
 contrib/inventory_builder/setup.cfg           |   3 -
 contrib/inventory_builder/setup.py            |  29 -
 .../inventory_builder/test-requirements.txt   |   3 -
 .../inventory_builder/tests/test_inventory.py | 595 ------------------
 contrib/inventory_builder/tox.ini             |  34 -
 7 files changed, 1147 deletions(-)
 delete mode 100644 contrib/inventory_builder/inventory.py
 delete mode 100644 contrib/inventory_builder/requirements.txt
 delete mode 100644 contrib/inventory_builder/setup.cfg
 delete mode 100644 contrib/inventory_builder/setup.py
 delete mode 100644 contrib/inventory_builder/test-requirements.txt
 delete mode 100644 contrib/inventory_builder/tests/test_inventory.py
 delete mode 100644 contrib/inventory_builder/tox.ini

diff --git a/contrib/inventory_builder/inventory.py b/contrib/inventory_builder/inventory.py
deleted file mode 100644
index 76e7c0c46..000000000
--- a/contrib/inventory_builder/inventory.py
+++ /dev/null
@@ -1,480 +0,0 @@
-#!/usr/bin/env python3
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#    http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-# implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# Usage: inventory.py ip1 [ip2 ...]
-# Examples: inventory.py 10.10.1.3 10.10.1.4 10.10.1.5
-#
-# Advanced usage:
-# Add another host after initial creation: inventory.py 10.10.1.5
-# Add range of hosts: inventory.py 10.10.1.3-10.10.1.5
-# Add hosts with different ip and access ip:
-# inventory.py 10.0.0.1,192.168.10.1 10.0.0.2,192.168.10.2 10.0.0.3,192.168.1.3
-# Add hosts with a specific hostname, ip, and optional access ip:
-# inventory.py first,10.0.0.1,192.168.10.1 second,10.0.0.2 last,10.0.0.3
-# Delete a host: inventory.py -10.10.1.3
-# Delete a host by id: inventory.py -node1
-#
-# Load a YAML or JSON file with inventory data: inventory.py load hosts.yaml
-# YAML file should be in the following format:
-#    group1:
-#      host1:
-#        ip: X.X.X.X
-#        var: val
-#    group2:
-#      host2:
-#        ip: X.X.X.X
-
-from collections import OrderedDict
-from ipaddress import ip_address
-from ruamel.yaml import YAML
-
-import os
-import re
-import subprocess
-import sys
-
-ROLES = ['all', 'kube_control_plane', 'kube_node', 'etcd', 'k8s_cluster',
-         'calico_rr']
-PROTECTED_NAMES = ROLES
-AVAILABLE_COMMANDS = ['help', 'print_cfg', 'print_ips', 'print_hostnames',
-                      'load', 'add']
-_boolean_states = {'1': True, 'yes': True, 'true': True, 'on': True,
-                   '0': False, 'no': False, 'false': False, 'off': False}
-yaml = YAML()
-yaml.Representer.add_representer(OrderedDict, yaml.Representer.represent_dict)
-
-
-def get_var_as_bool(name, default):
-    value = os.environ.get(name, '')
-    return _boolean_states.get(value.lower(), default)
-
-# Configurable as shell vars start
-
-
-CONFIG_FILE = os.environ.get("CONFIG_FILE", "./inventory/sample/hosts.yaml")
-# Remove the reference of KUBE_MASTERS after some deprecation cycles.
-KUBE_CONTROL_HOSTS = int(os.environ.get("KUBE_CONTROL_HOSTS",
-                         os.environ.get("KUBE_MASTERS", 2)))
-# Reconfigures cluster distribution at scale
-SCALE_THRESHOLD = int(os.environ.get("SCALE_THRESHOLD", 50))
-MASSIVE_SCALE_THRESHOLD = int(os.environ.get("MASSIVE_SCALE_THRESHOLD", 200))
-
-DEBUG = get_var_as_bool("DEBUG", True)
-HOST_PREFIX = os.environ.get("HOST_PREFIX", "node")
-USE_REAL_HOSTNAME = get_var_as_bool("USE_REAL_HOSTNAME", False)
-
-# Configurable as shell vars end
-
-
-class KubesprayInventory(object):
-
-    def __init__(self, changed_hosts=None, config_file=None):
-        self.config_file = config_file
-        self.yaml_config = {}
-        loadPreviousConfig = False
-        printHostnames = False
-        # See whether there are any commands to process
-        if changed_hosts and changed_hosts[0] in AVAILABLE_COMMANDS:
-            if changed_hosts[0] == "add":
-                loadPreviousConfig = True
-                changed_hosts = changed_hosts[1:]
-            elif changed_hosts[0] == "print_hostnames":
-                loadPreviousConfig = True
-                printHostnames = True
-            else:
-                self.parse_command(changed_hosts[0], changed_hosts[1:])
-                sys.exit(0)
-
-        # If the user wants to remove a node, we need to load the config anyway
-        if changed_hosts and changed_hosts[0][0] == "-":
-            loadPreviousConfig = True
-
-        if self.config_file and loadPreviousConfig:  # Load previous YAML file
-            try:
-                self.hosts_file = open(config_file, 'r')
-                self.yaml_config = yaml.load(self.hosts_file)
-            except OSError as e:
-                # I am assuming we are catching "cannot open file" exceptions
-                print(e)
-                sys.exit(1)
-
-        if printHostnames:
-            self.print_hostnames()
-            sys.exit(0)
-
-        self.ensure_required_groups(ROLES)
-
-        if changed_hosts:
-            changed_hosts = self.range2ips(changed_hosts)
-            self.hosts = self.build_hostnames(changed_hosts,
-                                              loadPreviousConfig)
-            self.purge_invalid_hosts(self.hosts.keys(), PROTECTED_NAMES)
-            self.set_all(self.hosts)
-            self.set_k8s_cluster()
-            etcd_hosts_count = 3 if len(self.hosts.keys()) >= 3 else 1
-            self.set_etcd(list(self.hosts.keys())[:etcd_hosts_count])
-            if len(self.hosts) >= SCALE_THRESHOLD:
-                self.set_kube_control_plane(list(self.hosts.keys())[
-                    etcd_hosts_count:(etcd_hosts_count + KUBE_CONTROL_HOSTS)])
-            else:
-                self.set_kube_control_plane(
-                  list(self.hosts.keys())[:KUBE_CONTROL_HOSTS])
-            self.set_kube_node(self.hosts.keys())
-            if len(self.hosts) >= SCALE_THRESHOLD:
-                self.set_calico_rr(list(self.hosts.keys())[:etcd_hosts_count])
-        else:  # Show help if no options
-            self.show_help()
-            sys.exit(0)
-
-        self.write_config(self.config_file)
-
-    def write_config(self, config_file):
-        if config_file:
-            with open(self.config_file, 'w') as f:
-                yaml.dump(self.yaml_config, f)
-
-        else:
-            print("WARNING: Unable to save config. Make sure you set "
-                  "CONFIG_FILE env var.")
-
-    def debug(self, msg):
-        if DEBUG:
-            print("DEBUG: {0}".format(msg))
-
-    def get_ip_from_opts(self, optstring):
-        if 'ip' in optstring:
-            return optstring['ip']
-        else:
-            raise ValueError("IP parameter not found in options")
-
-    def ensure_required_groups(self, groups):
-        for group in groups:
-            if group == 'all':
-                self.debug("Adding group {0}".format(group))
-                if group not in self.yaml_config:
-                    all_dict = OrderedDict([('hosts', OrderedDict({})),
-                                            ('children', OrderedDict({}))])
-                    self.yaml_config = {'all': all_dict}
-            else:
-                self.debug("Adding group {0}".format(group))
-                if group not in self.yaml_config['all']['children']:
-                    self.yaml_config['all']['children'][group] = {'hosts': {}}
-
-    def get_host_id(self, host):
-        '''Returns integer host ID (without padding) from a given hostname.'''
-        try:
-            short_hostname = host.split('.')[0]
-            return int(re.findall("\\d+$", short_hostname)[-1])
-        except IndexError:
-            raise ValueError("Host name must end in an integer")
-
-    # Keeps already specified hosts,
-    # and adds or removes the hosts provided as an argument
-    def build_hostnames(self, changed_hosts, loadPreviousConfig=False):
-        existing_hosts = OrderedDict()
-        highest_host_id = 0
-        # Load already existing hosts from the YAML
-        if loadPreviousConfig:
-            try:
-                for host in self.yaml_config['all']['hosts']:
-                    # Read configuration of an existing host
-                    hostConfig = self.yaml_config['all']['hosts'][host]
-                    existing_hosts[host] = hostConfig
-                    # If the existing host seems
-                    # to have been created automatically, detect its ID
-                    if host.startswith(HOST_PREFIX):
-                        host_id = self.get_host_id(host)
-                        if host_id > highest_host_id:
-                            highest_host_id = host_id
-            except Exception as e:
-                # I am assuming we are catching automatically
-                # created hosts without IDs
-                print(e)
-                sys.exit(1)
-
-        # FIXME(mattymo): Fix condition where delete then add reuses highest id
-        next_host_id = highest_host_id + 1
-        next_host = ""
-
-        all_hosts = existing_hosts.copy()
-        for host in changed_hosts:
-            # Delete the host from config the hostname/IP has a "-" prefix
-            if host[0] == "-":
-                realhost = host[1:]
-                if self.exists_hostname(all_hosts, realhost):
-                    self.debug("Marked {0} for deletion.".format(realhost))
-                    all_hosts.pop(realhost)
-                elif self.exists_ip(all_hosts, realhost):
-                    self.debug("Marked {0} for deletion.".format(realhost))
-                    self.delete_host_by_ip(all_hosts, realhost)
-            # Host/Argument starts with a digit,
-            # then we assume its an IP address
-            elif host[0].isdigit():
-                if ',' in host:
-                    ip, access_ip = host.split(',')
-                else:
-                    ip = host
-                    access_ip = host
-                if self.exists_hostname(all_hosts, host):
-                    self.debug("Skipping existing host {0}.".format(host))
-                    continue
-                elif self.exists_ip(all_hosts, ip):
-                    self.debug("Skipping existing host {0}.".format(ip))
-                    continue
-
-                if USE_REAL_HOSTNAME:
-                    cmd = ("ssh -oStrictHostKeyChecking=no "
-                           + access_ip + " 'hostname -s'")
-                    next_host = subprocess.check_output(cmd, shell=True)
-                    next_host = next_host.strip().decode('ascii')
-                else:
-                    # Generates a hostname because we have only an IP address
-                    next_host = "{0}{1}".format(HOST_PREFIX, next_host_id)
-                    next_host_id += 1
-                # Uses automatically generated node name
-                # in case we dont provide it.
-                all_hosts[next_host] = {'ansible_host': access_ip,
-                                        'ip': ip,
-                                        'access_ip': access_ip}
-            # Host/Argument starts with a letter, then we assume its a hostname
-            elif host[0].isalpha():
-                if ',' in host:
-                    try:
-                        hostname, ip, access_ip = host.split(',')
-                    except Exception:
-                        hostname, ip = host.split(',')
-                        access_ip = ip
-                if self.exists_hostname(all_hosts, host):
-                    self.debug("Skipping existing host {0}.".format(host))
-                    continue
-                elif self.exists_ip(all_hosts, ip):
-                    self.debug("Skipping existing host {0}.".format(ip))
-                    continue
-                all_hosts[hostname] = {'ansible_host': access_ip,
-                                       'ip': ip,
-                                       'access_ip': access_ip}
-        return all_hosts
-
-    # Expand IP ranges into individual addresses
-    def range2ips(self, hosts):
-        reworked_hosts = []
-
-        def ips(start_address, end_address):
-            try:
-                # Python 3.x
-                start = int(ip_address(start_address))
-                end = int(ip_address(end_address))
-            except Exception:
-                # Python 2.7
-                start = int(ip_address(str(start_address)))
-                end = int(ip_address(str(end_address)))
-            return [ip_address(ip).exploded for ip in range(start, end + 1)]
-
-        for host in hosts:
-            if '-' in host and not (host.startswith('-') or host[0].isalpha()):
-                start, end = host.strip().split('-')
-                try:
-                    reworked_hosts.extend(ips(start, end))
-                except ValueError:
-                    raise Exception("Range of ip_addresses isn't valid")
-            else:
-                reworked_hosts.append(host)
-        return reworked_hosts
-
-    def exists_hostname(self, existing_hosts, hostname):
-        return hostname in existing_hosts.keys()
-
-    def exists_ip(self, existing_hosts, ip):
-        for host_opts in existing_hosts.values():
-            if ip == self.get_ip_from_opts(host_opts):
-                return True
-        return False
-
-    def delete_host_by_ip(self, existing_hosts, ip):
-        for hostname, host_opts in existing_hosts.items():
-            if ip == self.get_ip_from_opts(host_opts):
-                del existing_hosts[hostname]
-                return
-        raise ValueError("Unable to find host by IP: {0}".format(ip))
-
-    def purge_invalid_hosts(self, hostnames, protected_names=[]):
-        for role in self.yaml_config['all']['children']:
-            if role != 'k8s_cluster' and self.yaml_config['all']['children'][role]['hosts']:  # noqa
-                all_hosts = self.yaml_config['all']['children'][role]['hosts'].copy()  # noqa
-                for host in all_hosts.keys():
-                    if host not in hostnames and host not in protected_names:
-                        self.debug(
-                            "Host {0} removed from role {1}".format(host, role))  # noqa
-                        del self.yaml_config['all']['children'][role]['hosts'][host]  # noqa
-        # purge from all
-        if self.yaml_config['all']['hosts']:
-            all_hosts = self.yaml_config['all']['hosts'].copy()
-            for host in all_hosts.keys():
-                if host not in hostnames and host not in protected_names:
-                    self.debug("Host {0} removed from role all".format(host))
-                    del self.yaml_config['all']['hosts'][host]
-
-    def add_host_to_group(self, group, host, opts=""):
-        self.debug("adding host {0} to group {1}".format(host, group))
-        if group == 'all':
-            if self.yaml_config['all']['hosts'] is None:
-                self.yaml_config['all']['hosts'] = {host: None}
-            self.yaml_config['all']['hosts'][host] = opts
-        elif group != 'k8s_cluster:children':
-            if self.yaml_config['all']['children'][group]['hosts'] is None:
-                self.yaml_config['all']['children'][group]['hosts'] = {
-                    host: None}
-            else:
-                self.yaml_config['all']['children'][group]['hosts'][host] = None  # noqa
-
-    def set_kube_control_plane(self, hosts):
-        for host in hosts:
-            self.add_host_to_group('kube_control_plane', host)
-
-    def set_all(self, hosts):
-        for host, opts in hosts.items():
-            self.add_host_to_group('all', host, opts)
-
-    def set_k8s_cluster(self):
-        k8s_cluster = {'children': {'kube_control_plane': None,
-                                    'kube_node': None}}
-        self.yaml_config['all']['children']['k8s_cluster'] = k8s_cluster
-
-    def set_calico_rr(self, hosts):
-        for host in hosts:
-            if host in self.yaml_config['all']['children']['kube_control_plane']: # noqa
-                self.debug("Not adding {0} to calico_rr group because it "
-                           "conflicts with kube_control_plane "
-                           "group".format(host))
-                continue
-            if host in self.yaml_config['all']['children']['kube_node']:
-                self.debug("Not adding {0} to calico_rr group because it "
-                           "conflicts with kube_node group".format(host))
-                continue
-            self.add_host_to_group('calico_rr', host)
-
-    def set_kube_node(self, hosts):
-        for host in hosts:
-            if len(self.yaml_config['all']['hosts']) >= SCALE_THRESHOLD:
-                if host in self.yaml_config['all']['children']['etcd']['hosts']:  # noqa
-                    self.debug("Not adding {0} to kube_node group because of "
-                               "scale deployment and host is in etcd "
-                               "group.".format(host))
-                    continue
-            if len(self.yaml_config['all']['hosts']) >= MASSIVE_SCALE_THRESHOLD:  # noqa
-                if host in self.yaml_config['all']['children']['kube_control_plane']['hosts']:  # noqa
-                    self.debug("Not adding {0} to kube_node group because of "
-                               "scale deployment and host is in "
-                               "kube_control_plane group.".format(host))
-                    continue
-            self.add_host_to_group('kube_node', host)
-
-    def set_etcd(self, hosts):
-        for host in hosts:
-            self.add_host_to_group('etcd', host)
-
-    def load_file(self, files=None):
-        '''Directly loads JSON to inventory.'''
-
-        if not files:
-            raise Exception("No input file specified.")
-
-        import json
-
-        for filename in list(files):
-            # Try JSON
-            try:
-                with open(filename, 'r') as f:
-                    data = json.load(f)
-            except ValueError:
-                raise Exception("Cannot read %s as JSON, or CSV", filename)
-
-            self.ensure_required_groups(ROLES)
-            self.set_k8s_cluster()
-            for group, hosts in data.items():
-                self.ensure_required_groups([group])
-                for host, opts in hosts.items():
-                    optstring = {'ansible_host': opts['ip'],
-                                 'ip': opts['ip'],
-                                 'access_ip': opts['ip']}
-                    self.add_host_to_group('all', host, optstring)
-                    self.add_host_to_group(group, host)
-            self.write_config(self.config_file)
-
-    def parse_command(self, command, args=None):
-        if command == 'help':
-            self.show_help()
-        elif command == 'print_cfg':
-            self.print_config()
-        elif command == 'print_ips':
-            self.print_ips()
-        elif command == 'print_hostnames':
-            self.print_hostnames()
-        elif command == 'load':
-            self.load_file(args)
-        else:
-            raise Exception("Invalid command specified.")
-
-    def show_help(self):
-        help_text = '''Usage: inventory.py ip1 [ip2 ...]
-Examples: inventory.py 10.10.1.3 10.10.1.4 10.10.1.5
-
-Available commands:
-help - Display this message
-print_cfg - Write inventory file to stdout
-print_ips - Write a space-delimited list of IPs from "all" group
-print_hostnames - Write a space-delimited list of Hostnames from "all" group
-add - Adds specified hosts into an already existing inventory
-
-Advanced usage:
-Create new or overwrite old inventory file: inventory.py 10.10.1.5
-Add another host after initial creation: inventory.py add 10.10.1.6
-Add range of hosts: inventory.py 10.10.1.3-10.10.1.5
-Add hosts with different ip and access ip: inventory.py 10.0.0.1,192.168.10.1 10.0.0.2,192.168.10.2 10.0.0.3,192.168.10.3
-Add hosts with a specific hostname, ip, and optional access ip: first,10.0.0.1,192.168.10.1 second,10.0.0.2 last,10.0.0.3
-Delete a host: inventory.py -10.10.1.3
-Delete a host by id: inventory.py -node1
-
-Configurable env vars:
-DEBUG                   Enable debug printing. Default: True
-CONFIG_FILE             File to write config to Default: ./inventory/sample/hosts.yaml
-HOST_PREFIX             Host prefix for generated hosts. Default: node
-KUBE_CONTROL_HOSTS      Set the number of kube-control-planes. Default: 2
-SCALE_THRESHOLD         Separate ETCD role if # of nodes >= 50
-MASSIVE_SCALE_THRESHOLD Separate K8s control-plane and ETCD if # of nodes >= 200
-'''  # noqa
-        print(help_text)
-
-    def print_config(self):
-        yaml.dump(self.yaml_config, sys.stdout)
-
-    def print_hostnames(self):
-        print(' '.join(self.yaml_config['all']['hosts'].keys()))
-
-    def print_ips(self):
-        ips = []
-        for host, opts in self.yaml_config['all']['hosts'].items():
-            ips.append(self.get_ip_from_opts(opts))
-        print(' '.join(ips))
-
-
-def main(argv=None):
-    if not argv:
-        argv = sys.argv[1:]
-    KubesprayInventory(argv, CONFIG_FILE)
-    return 0
-
-
-if __name__ == "__main__":
-    sys.exit(main())
diff --git a/contrib/inventory_builder/requirements.txt b/contrib/inventory_builder/requirements.txt
deleted file mode 100644
index c54501a4b..000000000
--- a/contrib/inventory_builder/requirements.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-configparser>=3.3.0
-ipaddress
-ruamel.yaml>=0.15.88
diff --git a/contrib/inventory_builder/setup.cfg b/contrib/inventory_builder/setup.cfg
deleted file mode 100644
index a775367e2..000000000
--- a/contrib/inventory_builder/setup.cfg
+++ /dev/null
@@ -1,3 +0,0 @@
-[metadata]
-name = kubespray-inventory-builder
-version = 0.1
diff --git a/contrib/inventory_builder/setup.py b/contrib/inventory_builder/setup.py
deleted file mode 100644
index 43c5ca1b4..000000000
--- a/contrib/inventory_builder/setup.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#    http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-# implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT
-import setuptools
-
-# In python < 2.7.4, a lazy loading of package `pbr` will break
-# setuptools if some other modules registered functions in `atexit`.
-# solution from: http://bugs.python.org/issue15881#msg170215
-try:
-    import multiprocessing  # noqa
-except ImportError:
-    pass
-
-setuptools.setup(
-    setup_requires=[],
-    pbr=False)
diff --git a/contrib/inventory_builder/test-requirements.txt b/contrib/inventory_builder/test-requirements.txt
deleted file mode 100644
index 98a662a4d..000000000
--- a/contrib/inventory_builder/test-requirements.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-hacking>=0.10.2
-mock>=1.3.0
-pytest>=2.8.0
diff --git a/contrib/inventory_builder/tests/test_inventory.py b/contrib/inventory_builder/tests/test_inventory.py
deleted file mode 100644
index 5d6649d68..000000000
--- a/contrib/inventory_builder/tests/test_inventory.py
+++ /dev/null
@@ -1,595 +0,0 @@
-# Copyright 2016 Mirantis, Inc.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-import inventory
-from io import StringIO
-import unittest
-from unittest import mock
-
-from collections import OrderedDict
-import sys
-
-path = "./contrib/inventory_builder/"
-if path not in sys.path:
-    sys.path.append(path)
-
-import inventory  # noqa
-
-
-class TestInventoryPrintHostnames(unittest.TestCase):
-
-    @mock.patch('ruamel.yaml.YAML.load')
-    def test_print_hostnames(self, load_mock):
-        mock_io = mock.mock_open(read_data='')
-        load_mock.return_value = OrderedDict({'all': {'hosts': {
-            'node1': {'ansible_host': '10.90.0.2',
-                      'ip': '10.90.0.2',
-                      'access_ip': '10.90.0.2'},
-            'node2': {'ansible_host': '10.90.0.3',
-                      'ip': '10.90.0.3',
-                      'access_ip': '10.90.0.3'}}}})
-        with mock.patch('builtins.open', mock_io):
-            with self.assertRaises(SystemExit) as cm:
-                with mock.patch('sys.stdout', new_callable=StringIO) as stdout:
-                    inventory.KubesprayInventory(
-                        changed_hosts=["print_hostnames"],
-                        config_file="file")
-            self.assertEqual("node1 node2\n", stdout.getvalue())
-            self.assertEqual(cm.exception.code, 0)
-
-
-class TestInventory(unittest.TestCase):
-    @mock.patch('inventory.sys')
-    def setUp(self, sys_mock):
-        sys_mock.exit = mock.Mock()
-        super(TestInventory, self).setUp()
-        self.data = ['10.90.3.2', '10.90.3.3', '10.90.3.4']
-        self.inv = inventory.KubesprayInventory()
-
-    def test_get_ip_from_opts(self):
-        optstring = {'ansible_host': '10.90.3.2',
-                     'ip': '10.90.3.2',
-                     'access_ip': '10.90.3.2'}
-        expected = "10.90.3.2"
-        result = self.inv.get_ip_from_opts(optstring)
-        self.assertEqual(expected, result)
-
-    def test_get_ip_from_opts_invalid(self):
-        optstring = "notanaddr=value something random!chars:D"
-        self.assertRaisesRegex(ValueError, "IP parameter not found",
-                               self.inv.get_ip_from_opts, optstring)
-
-    def test_ensure_required_groups(self):
-        groups = ['group1', 'group2']
-        self.inv.ensure_required_groups(groups)
-        for group in groups:
-            self.assertIn(group, self.inv.yaml_config['all']['children'])
-
-    def test_get_host_id(self):
-        hostnames = ['node99', 'no99de01', '01node01', 'node1.domain',
-                     'node3.xyz123.aaa']
-        expected = [99, 1, 1, 1, 3]
-        for hostname, expected in zip(hostnames, expected):
-            result = self.inv.get_host_id(hostname)
-            self.assertEqual(expected, result)
-
-    def test_get_host_id_invalid(self):
-        bad_hostnames = ['node', 'no99de', '01node', 'node.111111']
-        for hostname in bad_hostnames:
-            self.assertRaisesRegex(ValueError, "Host name must end in an",
-                                   self.inv.get_host_id, hostname)
-
-    def test_build_hostnames_add_duplicate(self):
-        changed_hosts = ['10.90.0.2']
-        expected = OrderedDict([('node3',
-                                 {'ansible_host': '10.90.0.2',
-                                  'ip': '10.90.0.2',
-                                  'access_ip': '10.90.0.2'})])
-        self.inv.yaml_config['all']['hosts'] = expected
-        result = self.inv.build_hostnames(changed_hosts, True)
-        self.assertEqual(expected, result)
-
-    def test_build_hostnames_add_two(self):
-        changed_hosts = ['10.90.0.2', '10.90.0.3']
-        expected = OrderedDict([
-            ('node1', {'ansible_host': '10.90.0.2',
-                       'ip': '10.90.0.2',
-                       'access_ip': '10.90.0.2'}),
-            ('node2', {'ansible_host': '10.90.0.3',
-                       'ip': '10.90.0.3',
-                       'access_ip': '10.90.0.3'})])
-        self.inv.yaml_config['all']['hosts'] = OrderedDict()
-        result = self.inv.build_hostnames(changed_hosts)
-        self.assertEqual(expected, result)
-
-    def test_build_hostnames_add_three(self):
-        changed_hosts = ['10.90.0.2', '10.90.0.3', '10.90.0.4']
-        expected = OrderedDict([
-            ('node1', {'ansible_host': '10.90.0.2',
-                       'ip': '10.90.0.2',
-                       'access_ip': '10.90.0.2'}),
-            ('node2', {'ansible_host': '10.90.0.3',
-                       'ip': '10.90.0.3',
-                       'access_ip': '10.90.0.3'}),
-            ('node3', {'ansible_host': '10.90.0.4',
-                       'ip': '10.90.0.4',
-                       'access_ip': '10.90.0.4'})])
-        result = self.inv.build_hostnames(changed_hosts)
-        self.assertEqual(expected, result)
-
-    def test_build_hostnames_add_one(self):
-        changed_hosts = ['10.90.0.2']
-        expected = OrderedDict([('node1',
-                                 {'ansible_host': '10.90.0.2',
-                                  'ip': '10.90.0.2',
-                                  'access_ip': '10.90.0.2'})])
-        result = self.inv.build_hostnames(changed_hosts)
-        self.assertEqual(expected, result)
-
-    def test_build_hostnames_delete_first(self):
-        changed_hosts = ['-10.90.0.2']
-        existing_hosts = OrderedDict([
-            ('node1', {'ansible_host': '10.90.0.2',
-                       'ip': '10.90.0.2',
-                       'access_ip': '10.90.0.2'}),
-            ('node2', {'ansible_host': '10.90.0.3',
-                       'ip': '10.90.0.3',
-                       'access_ip': '10.90.0.3'})])
-        self.inv.yaml_config['all']['hosts'] = existing_hosts
-        expected = OrderedDict([
-            ('node2', {'ansible_host': '10.90.0.3',
-                       'ip': '10.90.0.3',
-                       'access_ip': '10.90.0.3'})])
-        result = self.inv.build_hostnames(changed_hosts, True)
-        self.assertEqual(expected, result)
-
-    def test_build_hostnames_delete_by_hostname(self):
-        changed_hosts = ['-node1']
-        existing_hosts = OrderedDict([
-            ('node1', {'ansible_host': '10.90.0.2',
-                       'ip': '10.90.0.2',
-                       'access_ip': '10.90.0.2'}),
-            ('node2', {'ansible_host': '10.90.0.3',
-                       'ip': '10.90.0.3',
-                       'access_ip': '10.90.0.3'})])
-        self.inv.yaml_config['all']['hosts'] = existing_hosts
-        expected = OrderedDict([
-            ('node2', {'ansible_host': '10.90.0.3',
-                       'ip': '10.90.0.3',
-                       'access_ip': '10.90.0.3'})])
-        result = self.inv.build_hostnames(changed_hosts, True)
-        self.assertEqual(expected, result)
-
-    def test_exists_hostname_positive(self):
-        hostname = 'node1'
-        expected = True
-        existing_hosts = OrderedDict([
-            ('node1', {'ansible_host': '10.90.0.2',
-                       'ip': '10.90.0.2',
-                       'access_ip': '10.90.0.2'}),
-            ('node2', {'ansible_host': '10.90.0.3',
-                       'ip': '10.90.0.3',
-                       'access_ip': '10.90.0.3'})])
-        result = self.inv.exists_hostname(existing_hosts, hostname)
-        self.assertEqual(expected, result)
-
-    def test_exists_hostname_negative(self):
-        hostname = 'node99'
-        expected = False
-        existing_hosts = OrderedDict([
-            ('node1', {'ansible_host': '10.90.0.2',
-                       'ip': '10.90.0.2',
-                       'access_ip': '10.90.0.2'}),
-            ('node2', {'ansible_host': '10.90.0.3',
-                       'ip': '10.90.0.3',
-                       'access_ip': '10.90.0.3'})])
-        result = self.inv.exists_hostname(existing_hosts, hostname)
-        self.assertEqual(expected, result)
-
-    def test_exists_ip_positive(self):
-        ip = '10.90.0.2'
-        expected = True
-        existing_hosts = OrderedDict([
-            ('node1', {'ansible_host': '10.90.0.2',
-                       'ip': '10.90.0.2',
-                       'access_ip': '10.90.0.2'}),
-            ('node2', {'ansible_host': '10.90.0.3',
-                       'ip': '10.90.0.3',
-                       'access_ip': '10.90.0.3'})])
-        result = self.inv.exists_ip(existing_hosts, ip)
-        self.assertEqual(expected, result)
-
-    def test_exists_ip_negative(self):
-        ip = '10.90.0.200'
-        expected = False
-        existing_hosts = OrderedDict([
-            ('node1', {'ansible_host': '10.90.0.2',
-                       'ip': '10.90.0.2',
-                       'access_ip': '10.90.0.2'}),
-            ('node2', {'ansible_host': '10.90.0.3',
-                       'ip': '10.90.0.3',
-                       'access_ip': '10.90.0.3'})])
-        result = self.inv.exists_ip(existing_hosts, ip)
-        self.assertEqual(expected, result)
-
-    def test_delete_host_by_ip_positive(self):
-        ip = '10.90.0.2'
-        expected = OrderedDict([
-            ('node2', {'ansible_host': '10.90.0.3',
-                       'ip': '10.90.0.3',
-                       'access_ip': '10.90.0.3'})])
-        existing_hosts = OrderedDict([
-            ('node1', {'ansible_host': '10.90.0.2',
-                       'ip': '10.90.0.2',
-                       'access_ip': '10.90.0.2'}),
-            ('node2', {'ansible_host': '10.90.0.3',
-                       'ip': '10.90.0.3',
-                       'access_ip': '10.90.0.3'})])
-        self.inv.delete_host_by_ip(existing_hosts, ip)
-        self.assertEqual(expected, existing_hosts)
-
-    def test_delete_host_by_ip_negative(self):
-        ip = '10.90.0.200'
-        existing_hosts = OrderedDict([
-            ('node1', {'ansible_host': '10.90.0.2',
-                       'ip': '10.90.0.2',
-                       'access_ip': '10.90.0.2'}),
-            ('node2', {'ansible_host': '10.90.0.3',
-                       'ip': '10.90.0.3',
-                       'access_ip': '10.90.0.3'})])
-        self.assertRaisesRegex(ValueError, "Unable to find host",
-                               self.inv.delete_host_by_ip, existing_hosts, ip)
-
-    def test_purge_invalid_hosts(self):
-        proper_hostnames = ['node1', 'node2']
-        bad_host = 'doesnotbelong2'
-        existing_hosts = OrderedDict([
-            ('node1', {'ansible_host': '10.90.0.2',
-                       'ip': '10.90.0.2',
-                       'access_ip': '10.90.0.2'}),
-            ('node2', {'ansible_host': '10.90.0.3',
-                       'ip': '10.90.0.3',
-                       'access_ip': '10.90.0.3'}),
-            ('doesnotbelong2', {'whateveropts=ilike'})])
-        self.inv.yaml_config['all']['hosts'] = existing_hosts
-        self.inv.purge_invalid_hosts(proper_hostnames)
-        self.assertNotIn(
-            bad_host, self.inv.yaml_config['all']['hosts'].keys())
-
-    def test_add_host_to_group(self):
-        group = 'etcd'
-        host = 'node1'
-        opts = {'ip': '10.90.0.2'}
-
-        self.inv.add_host_to_group(group, host, opts)
-        self.assertEqual(
-            self.inv.yaml_config['all']['children'][group]['hosts'].get(host),
-            None)
-
-    def test_set_kube_control_plane(self):
-        group = 'kube_control_plane'
-        host = 'node1'
-
-        self.inv.set_kube_control_plane([host])
-        self.assertIn(
-            host, self.inv.yaml_config['all']['children'][group]['hosts'])
-
-    def test_set_all(self):
-        hosts = OrderedDict([
-            ('node1', 'opt1'),
-            ('node2', 'opt2')])
-
-        self.inv.set_all(hosts)
-        for host, opt in hosts.items():
-            self.assertEqual(
-                self.inv.yaml_config['all']['hosts'].get(host), opt)
-
-    def test_set_k8s_cluster(self):
-        group = 'k8s_cluster'
-        expected_hosts = ['kube_node', 'kube_control_plane']
-
-        self.inv.set_k8s_cluster()
-        for host in expected_hosts:
-            self.assertIn(
-                host,
-                self.inv.yaml_config['all']['children'][group]['children'])
-
-    def test_set_kube_node(self):
-        group = 'kube_node'
-        host = 'node1'
-
-        self.inv.set_kube_node([host])
-        self.assertIn(
-            host, self.inv.yaml_config['all']['children'][group]['hosts'])
-
-    def test_set_etcd(self):
-        group = 'etcd'
-        host = 'node1'
-
-        self.inv.set_etcd([host])
-        self.assertIn(
-            host, self.inv.yaml_config['all']['children'][group]['hosts'])
-
-    def test_scale_scenario_one(self):
-        num_nodes = 50
-        hosts = OrderedDict()
-
-        for hostid in range(1, num_nodes+1):
-            hosts["node" + str(hostid)] = ""
-
-        self.inv.set_all(hosts)
-        self.inv.set_etcd(list(hosts.keys())[0:3])
-        self.inv.set_kube_control_plane(list(hosts.keys())[0:2])
-        self.inv.set_kube_node(hosts.keys())
-        for h in range(3):
-            self.assertFalse(
-                list(hosts.keys())[h] in
-                self.inv.yaml_config['all']['children']['kube_node']['hosts'])
-
-    def test_scale_scenario_two(self):
-        num_nodes = 500
-        hosts = OrderedDict()
-
-        for hostid in range(1, num_nodes+1):
-            hosts["node" + str(hostid)] = ""
-
-        self.inv.set_all(hosts)
-        self.inv.set_etcd(list(hosts.keys())[0:3])
-        self.inv.set_kube_control_plane(list(hosts.keys())[3:5])
-        self.inv.set_kube_node(hosts.keys())
-        for h in range(5):
-            self.assertFalse(
-                list(hosts.keys())[h] in
-                self.inv.yaml_config['all']['children']['kube_node']['hosts'])
-
-    def test_range2ips_range(self):
-        changed_hosts = ['10.90.0.2', '10.90.0.4-10.90.0.6', '10.90.0.8']
-        expected = ['10.90.0.2',
-                    '10.90.0.4',
-                    '10.90.0.5',
-                    '10.90.0.6',
-                    '10.90.0.8']
-        result = self.inv.range2ips(changed_hosts)
-        self.assertEqual(expected, result)
-
-    def test_range2ips_incorrect_range(self):
-        host_range = ['10.90.0.4-a.9b.c.e']
-        self.assertRaisesRegex(Exception, "Range of ip_addresses isn't valid",
-                               self.inv.range2ips, host_range)
-
-    def test_build_hostnames_create_with_one_different_ips(self):
-        changed_hosts = ['10.90.0.2,192.168.0.2']
-        expected = OrderedDict([('node1',
-                                 {'ansible_host': '192.168.0.2',
-                                  'ip': '10.90.0.2',
-                                  'access_ip': '192.168.0.2'})])
-        result = self.inv.build_hostnames(changed_hosts)
-        self.assertEqual(expected, result)
-
-    def test_build_hostnames_create_with_two_different_ips(self):
-        changed_hosts = ['10.90.0.2,192.168.0.2', '10.90.0.3,192.168.0.3']
-        expected = OrderedDict([
-            ('node1', {'ansible_host': '192.168.0.2',
-                       'ip': '10.90.0.2',
-                       'access_ip': '192.168.0.2'}),
-            ('node2', {'ansible_host': '192.168.0.3',
-                       'ip': '10.90.0.3',
-                       'access_ip': '192.168.0.3'})])
-        result = self.inv.build_hostnames(changed_hosts)
-        self.assertEqual(expected, result)
-
-    def test_build_hostnames_create_with_three_different_ips(self):
-        changed_hosts = ['10.90.0.2,192.168.0.2',
-                         '10.90.0.3,192.168.0.3',
-                         '10.90.0.4,192.168.0.4']
-        expected = OrderedDict([
-            ('node1', {'ansible_host': '192.168.0.2',
-                       'ip': '10.90.0.2',
-                       'access_ip': '192.168.0.2'}),
-            ('node2', {'ansible_host': '192.168.0.3',
-                       'ip': '10.90.0.3',
-                       'access_ip': '192.168.0.3'}),
-            ('node3', {'ansible_host': '192.168.0.4',
-                       'ip': '10.90.0.4',
-                       'access_ip': '192.168.0.4'})])
-        result = self.inv.build_hostnames(changed_hosts)
-        self.assertEqual(expected, result)
-
-    def test_build_hostnames_overwrite_one_with_different_ips(self):
-        changed_hosts = ['10.90.0.2,192.168.0.2']
-        expected = OrderedDict([('node1',
-                                 {'ansible_host': '192.168.0.2',
-                                  'ip': '10.90.0.2',
-                                  'access_ip': '192.168.0.2'})])
-        existing = OrderedDict([('node5',
-                                 {'ansible_host': '192.168.0.5',
-                                  'ip': '10.90.0.5',
-                                  'access_ip': '192.168.0.5'})])
-        self.inv.yaml_config['all']['hosts'] = existing
-        result = self.inv.build_hostnames(changed_hosts)
-        self.assertEqual(expected, result)
-
-    def test_build_hostnames_overwrite_three_with_different_ips(self):
-        changed_hosts = ['10.90.0.2,192.168.0.2']
-        expected = OrderedDict([('node1',
-                                 {'ansible_host': '192.168.0.2',
-                                  'ip': '10.90.0.2',
-                                  'access_ip': '192.168.0.2'})])
-        existing = OrderedDict([
-            ('node3', {'ansible_host': '192.168.0.3',
-                       'ip': '10.90.0.3',
-                       'access_ip': '192.168.0.3'}),
-            ('node4', {'ansible_host': '192.168.0.4',
-                       'ip': '10.90.0.4',
-                       'access_ip': '192.168.0.4'}),
-            ('node5', {'ansible_host': '192.168.0.5',
-                       'ip': '10.90.0.5',
-                       'access_ip': '192.168.0.5'})])
-        self.inv.yaml_config['all']['hosts'] = existing
-        result = self.inv.build_hostnames(changed_hosts)
-        self.assertEqual(expected, result)
-
-    def test_build_hostnames_different_ips_add_duplicate(self):
-        changed_hosts = ['10.90.0.2,192.168.0.2']
-        expected = OrderedDict([('node3',
-                                 {'ansible_host': '192.168.0.2',
-                                  'ip': '10.90.0.2',
-                                  'access_ip': '192.168.0.2'})])
-        existing = expected
-        self.inv.yaml_config['all']['hosts'] = existing
-        result = self.inv.build_hostnames(changed_hosts, True)
-        self.assertEqual(expected, result)
-
-    def test_build_hostnames_add_two_different_ips_into_one_existing(self):
-        changed_hosts = ['10.90.0.3,192.168.0.3', '10.90.0.4,192.168.0.4']
-        expected = OrderedDict([
-            ('node2', {'ansible_host': '192.168.0.2',
-                       'ip': '10.90.0.2',
-                       'access_ip': '192.168.0.2'}),
-            ('node3', {'ansible_host': '192.168.0.3',
-                       'ip': '10.90.0.3',
-                       'access_ip': '192.168.0.3'}),
-            ('node4', {'ansible_host': '192.168.0.4',
-                       'ip': '10.90.0.4',
-                       'access_ip': '192.168.0.4'})])
-
-        existing = OrderedDict([
-            ('node2', {'ansible_host': '192.168.0.2',
-                       'ip': '10.90.0.2',
-                       'access_ip': '192.168.0.2'})])
-        self.inv.yaml_config['all']['hosts'] = existing
-        result = self.inv.build_hostnames(changed_hosts, True)
-        self.assertEqual(expected, result)
-
-    def test_build_hostnames_add_two_different_ips_into_two_existing(self):
-        changed_hosts = ['10.90.0.4,192.168.0.4', '10.90.0.5,192.168.0.5']
-        expected = OrderedDict([
-            ('node2', {'ansible_host': '192.168.0.2',
-                       'ip': '10.90.0.2',
-                       'access_ip': '192.168.0.2'}),
-            ('node3', {'ansible_host': '192.168.0.3',
-                       'ip': '10.90.0.3',
-                       'access_ip': '192.168.0.3'}),
-            ('node4', {'ansible_host': '192.168.0.4',
-                       'ip': '10.90.0.4',
-                       'access_ip': '192.168.0.4'}),
-            ('node5', {'ansible_host': '192.168.0.5',
-                       'ip': '10.90.0.5',
-                       'access_ip': '192.168.0.5'})])
-
-        existing = OrderedDict([
-            ('node2', {'ansible_host': '192.168.0.2',
-                       'ip': '10.90.0.2',
-                       'access_ip': '192.168.0.2'}),
-            ('node3', {'ansible_host': '192.168.0.3',
-                       'ip': '10.90.0.3',
-                       'access_ip': '192.168.0.3'})])
-        self.inv.yaml_config['all']['hosts'] = existing
-        result = self.inv.build_hostnames(changed_hosts, True)
-        self.assertEqual(expected, result)
-
-    def test_build_hostnames_add_two_different_ips_into_three_existing(self):
-        changed_hosts = ['10.90.0.5,192.168.0.5', '10.90.0.6,192.168.0.6']
-        expected = OrderedDict([
-            ('node2', {'ansible_host': '192.168.0.2',
-                       'ip': '10.90.0.2',
-                       'access_ip': '192.168.0.2'}),
-            ('node3', {'ansible_host': '192.168.0.3',
-                       'ip': '10.90.0.3',
-                       'access_ip': '192.168.0.3'}),
-            ('node4', {'ansible_host': '192.168.0.4',
-                       'ip': '10.90.0.4',
-                       'access_ip': '192.168.0.4'}),
-            ('node5', {'ansible_host': '192.168.0.5',
-                       'ip': '10.90.0.5',
-                       'access_ip': '192.168.0.5'}),
-            ('node6', {'ansible_host': '192.168.0.6',
-                       'ip': '10.90.0.6',
-                       'access_ip': '192.168.0.6'})])
-
-        existing = OrderedDict([
-            ('node2', {'ansible_host': '192.168.0.2',
-                       'ip': '10.90.0.2',
-                       'access_ip': '192.168.0.2'}),
-            ('node3', {'ansible_host': '192.168.0.3',
-                       'ip': '10.90.0.3',
-                       'access_ip': '192.168.0.3'}),
-            ('node4', {'ansible_host': '192.168.0.4',
-                       'ip': '10.90.0.4',
-                       'access_ip': '192.168.0.4'})])
-        self.inv.yaml_config['all']['hosts'] = existing
-        result = self.inv.build_hostnames(changed_hosts, True)
-        self.assertEqual(expected, result)
-
-    # Add two IP addresses into a config that has
-    # three already defined IP addresses. One of the IP addresses
-    # is a duplicate.
-    def test_build_hostnames_add_two_duplicate_one_overlap(self):
-        changed_hosts = ['10.90.0.4,192.168.0.4', '10.90.0.5,192.168.0.5']
-        expected = OrderedDict([
-            ('node2', {'ansible_host': '192.168.0.2',
-                       'ip': '10.90.0.2',
-                       'access_ip': '192.168.0.2'}),
-            ('node3', {'ansible_host': '192.168.0.3',
-                       'ip': '10.90.0.3',
-                       'access_ip': '192.168.0.3'}),
-            ('node4', {'ansible_host': '192.168.0.4',
-                       'ip': '10.90.0.4',
-                       'access_ip': '192.168.0.4'}),
-            ('node5', {'ansible_host': '192.168.0.5',
-                       'ip': '10.90.0.5',
-                       'access_ip': '192.168.0.5'})])
-
-        existing = OrderedDict([
-            ('node2', {'ansible_host': '192.168.0.2',
-                       'ip': '10.90.0.2',
-                       'access_ip': '192.168.0.2'}),
-            ('node3', {'ansible_host': '192.168.0.3',
-                       'ip': '10.90.0.3',
-                       'access_ip': '192.168.0.3'}),
-            ('node4', {'ansible_host': '192.168.0.4',
-                       'ip': '10.90.0.4',
-                       'access_ip': '192.168.0.4'})])
-        self.inv.yaml_config['all']['hosts'] = existing
-        result = self.inv.build_hostnames(changed_hosts, True)
-        self.assertEqual(expected, result)
-
-    # Add two duplicate IP addresses into a config that has
-    # three already defined IP addresses
-    def test_build_hostnames_add_two_duplicate_two_overlap(self):
-        changed_hosts = ['10.90.0.3,192.168.0.3', '10.90.0.4,192.168.0.4']
-        expected = OrderedDict([
-            ('node2', {'ansible_host': '192.168.0.2',
-                       'ip': '10.90.0.2',
-                       'access_ip': '192.168.0.2'}),
-            ('node3', {'ansible_host': '192.168.0.3',
-                       'ip': '10.90.0.3',
-                       'access_ip': '192.168.0.3'}),
-            ('node4', {'ansible_host': '192.168.0.4',
-                       'ip': '10.90.0.4',
-                       'access_ip': '192.168.0.4'})])
-
-        existing = OrderedDict([
-            ('node2', {'ansible_host': '192.168.0.2',
-                       'ip': '10.90.0.2',
-                       'access_ip': '192.168.0.2'}),
-            ('node3', {'ansible_host': '192.168.0.3',
-                       'ip': '10.90.0.3',
-                       'access_ip': '192.168.0.3'}),
-            ('node4', {'ansible_host': '192.168.0.4',
-                       'ip': '10.90.0.4',
-                       'access_ip': '192.168.0.4'})])
-        self.inv.yaml_config['all']['hosts'] = existing
-        result = self.inv.build_hostnames(changed_hosts, True)
-        self.assertEqual(expected, result)
diff --git a/contrib/inventory_builder/tox.ini b/contrib/inventory_builder/tox.ini
deleted file mode 100644
index c9c70428c..000000000
--- a/contrib/inventory_builder/tox.ini
+++ /dev/null
@@ -1,34 +0,0 @@
-[tox]
-minversion = 1.6
-skipsdist = True
-envlist = pep8
-
-[testenv]
-allowlist_externals = py.test
-usedevelop = True
-deps =
-    -r{toxinidir}/requirements.txt
-    -r{toxinidir}/test-requirements.txt
-setenv = VIRTUAL_ENV={envdir}
-passenv =
-    http_proxy
-    HTTP_PROXY
-    https_proxy
-    HTTPS_PROXY
-    no_proxy
-    NO_PROXY
-commands = pytest -vv #{posargs:./tests}
-
-[testenv:pep8]
-usedevelop = False
-allowlist_externals = bash
-commands =
-    bash -c "find {toxinidir}/* -type f -name '*.py' -print0 | xargs -0 flake8"
-
-[testenv:venv]
-commands = {posargs}
-
-[flake8]
-show-source = true
-builtins = _
-exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg
-- 
GitLab